<commit_before>'''
Installation of Python Packages Using pip
=========================================

These states manage system installed python packages. Note that pip must be
installed for these states to be available, so pip states should include a
requisite to a pkg.installed state for the package which provides pip
(``python-pip`` in most cases). Example:

.. code-block:: yaml

    python-pip:
      pkg.installed

    virtualenvwrapper:
      pip.installed:
        - require:
          - pkg: python-pip
'''

# Import python libs
import urlparse

# Import salt libs
import salt.utils
from salt.exceptions import CommandExecutionError, CommandNotFoundError

def __virtual__():
    '''
    Only load if the pip module is available in __salt__
    '''
    return 'pip' if 'pip.list' in __salt__ else False

def installed(name,
              pip_bin=None,
              requirements=None,
              env=None,
              bin_env=None,
              log=None,
              proxy=None,
              timeout=None,
              repo=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,
              exists_action=None,
              no_deps=False,
              no_install=False,
              no_download=False,
              install_options=None,
              user=None,
              runas=None,
              no_chown=False,
              cwd=None,
              pre_releases=False,
              __env__='base'):
    '''
    Make sure the package is installed

    name
        The name of the python package to install
    user
        The user under which to run pip
    pip_bin : None
        Deprecated, use bin_env
    env : None
        Deprecated, use bin_env
    bin_env : None
        the pip executable or virtualenv to use

    '''
    if pip_bin and not bin_env:
        bin_env = pip_bin
    elif env and not bin_env:
        bin_env = env

    scheme, netloc, path, query, fragment = urlparse.urlsplit(name)
    if scheme and netloc:
        # parse as VCS url
        prefix = path.lstrip('/').split('@', 1)[0]
        if scheme.startswith("git+"):
            prefix = prefix.rstrip(".git")
    else:
        # Pull off any requirements specifiers
        prefix = name.split('=')[0].split('<')[0].split('>')[0].strip()

    ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}
    if runas is not None:
        # The user is using a deprecated argument, warn!
        msg = (
            'The \'runas\' argument to pip.installed is deprecated, and will '
            'be removed in 0.18.0. Please use \'user\' instead.'
        )
        salt.utils.warn_until((0, 18), msg)
        ret.setdefault('warnings', []).append(msg)

    # "There can only be one"
    if runas is not None and user:
        raise CommandExecutionError(
            'The \'runas\' and \'user\' arguments are mutually exclusive. '
            'Please use \'user\' as \'runas\' is being deprecated.'
        )
    # Support deprecated 'runas' arg
    elif runas is not None and not user:
        user = runas

    try:
        pip_list = __salt__['pip.list'](prefix, bin_env, user=user, cwd=cwd)
    except (CommandNotFoundError, CommandExecutionError) as err:
        ret['result'] = False
        ret['comment'] = 'Error installing \'{0}\': {1}'.format(name, err)
        return ret

    if ignore_installed is False and prefix.lower() in (p.lower()
                                                        for p in pip_list):
        if force_reinstall is False and upgrade is False:
            ret['result'] = True
            ret['comment'] = 'Package already installed'
            return ret

    if __opts__['test']:
        ret['result'] = None
        ret['comment'] = 'Python package {0} is set to be installed'.format(
                name)
        return ret

    # Replace commas (used for version ranges) with semicolons (which are not
    # supported) in name so it does not treat them as multiple packages.  Comma
    # will be re-added in pip.install call.  Wrap in double quotes to allow for
    # version ranges
    name = '"' + name.replace(',', ';') + '"'

    if repo:
        name = repo

    # If a requirements file is specified, only install the contents of the
    # requirements file. Similarly, using the --editable flag with pip should
    # also ignore the "name" parameter.
    if requirements or editable:
        name = ''

    pip_install_call = __salt__['pip.install'](
        pkgs=name,
        requirements=requirements,
        bin_env=bin_env,
        log=log,
        proxy=proxy,
        timeout=timeout,
        editable=editable,
        find_links=find_links,
        index_url=index_url,
        extra_index_url=extra_index_url,
        no_index=no_index,
        mirrors=mirrors,
        build=build,
        target=target,
        download=download,
        download_cache=download_cache,
        source=source,
        upgrade=upgrade,
        force_reinstall=force_reinstall,
        ignore_installed=ignore_installed,
        exists_action=exists_action,
        no_deps=no_deps,
        no_install=no_install,
        no_download=no_download,
        install_options=install_options,
        user=user,
        no_chown=no_chown,
        cwd=cwd,
        pre_releases=pre_releases,
        __env__=__env__
    )

    if pip_install_call and (pip_install_call['retcode'] == 0):
        ret['result'] = True

        pkg_list = __salt__['pip.list'](prefix, bin_env, user=user, cwd=cwd)
        if not pkg_list:
            ret['comment'] = (
                'There was no error installing package \'{0}\' although '
                'it does not show when calling \'pip.freeze\'.'.format(name)
            )
            ret['changes']['{0}==???'.format(name)] = 'Installed'
            return ret

        version = list(pkg_list.values())[0]
        pkg_name = next(iter(pkg_list))
        ret['changes']['{0}=={1}'.format(pkg_name, version)] = 'Installed'
        ret['comment'] = 'Package was successfully installed'
    elif pip_install_call:
        ret['result'] = False
        ret['comment'] = ('Failed to install package {0}. '
                          'Error: {1} {2}').format(name,
                                                   pip_install_call['stdout'],
                                                   pip_install_call['stderr'])
    else:
        ret['result'] = False
        ret['comment'] = 'Could not install package'

    return ret

def removed(name,
            requirements=None,
            bin_env=None,
            log=None,
            proxy=None,
            timeout=None,
            user=None,
            runas=None,
            cwd=None,
            __env__='base'):
    '''
    Make sure that a package is not installed.

    name
        The name of the package to uninstall
    user
        The user under which to run pip
    bin_env : None
        the pip executable or virtualenenv to use
    '''
    ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}

    if runas is not None:
        # The user is using a deprecated argument, warn!
        msg = (
            'The \'runas\' argument to pip.installed is deprecated, and will '
            'be removed in 0.18.0. Please use \'user\' instead.'
        )
        salt.utils.warn_until((0, 18), msg)
        ret.setdefault('warnings', []).append(msg)

    # "There can only be one"
    if runas is not None and user:
        raise CommandExecutionError(
            'The \'runas\' and \'user\' arguments are mutually exclusive. '
            'Please use \'user\' as \'runas\' is being deprecated.'
        )
    # Support deprecated 'runas' arg
    elif runas is not None and not user:
        user = runas

    try:
        pip_list = __salt__['pip.list'](bin_env=bin_env, user=user, cwd=cwd)
    except (CommandExecutionError, CommandNotFoundError) as err:
        ret['result'] = False
        ret['comment'] = 'Error uninstalling \'{0}\': {1}'.format(name, err)
        return ret

    if name not in pip_list:
        ret['result'] = True
        ret['comment'] = 'Package is not installed.'
        return ret

    if __opts__['test']:
        ret['result'] = None
        ret['comment'] = 'Package {0} is set to be removed'.format(name)
        return ret

    if __salt__['pip.uninstall'](pkgs=name,
                                 requirements=requirements,
                                 bin_env=bin_env,
                                 log=log,
                                 proxy=proxy,
                                 timeout=timeout,
                                 user=user,
                                 cwd=cwd,
                                 __env__='base'):
        ret['result'] = True
        ret['changes'][name] = 'Removed'
        ret['comment'] = 'Package was successfully removed.'
    else:
        ret['result'] = False
        ret['comment'] = 'Could not remove package.'
    return ret
<commit_msg>Fix version specification in pip.installed states<commit_after>'''
Installation of Python Packages Using pip
=========================================

These states manage system installed python packages. Note that pip must be
installed for these states to be available, so pip states should include a
requisite to a pkg.installed state for the package which provides pip
(``python-pip`` in most cases). Example:

.. code-block:: yaml

    python-pip:
      pkg.installed

    virtualenvwrapper:
      pip.installed:
        - require:
          - pkg: python-pip
'''

# Import python libs
import re
import urlparse

# Import salt libs
import salt.utils
from salt.exceptions import CommandExecutionError, CommandNotFoundError

def __virtual__():
    '''
    Only load if the pip module is available in __salt__
    '''
    return 'pip' if 'pip.list' in __salt__ else False

def _find_key(prefix, pip_list):
    '''
    Does a case-insensitive match in the pip_list for the desired package.
    '''
    try:
        match = next(
            iter(x for x in pip_list if x.lower() == prefix.lower())
        )
    except StopIteration:
        return None
    else:
        return match

def _fulfills_version_spec(version, version_spec):
    '''
    Check version number against version specification info and return a
    boolean value based on whether or not the version number meets the
    specified version.
    '''
    for oper, spec in (version_spec[0:2], version_spec[2:4]):
        if oper is None:
            continue
        if not salt.utils.compare_versions(ver1=version, oper=oper, ver2=spec):
            return False
    return True

def installed(name,
              pip_bin=None,
              requirements=None,
              env=None,
              bin_env=None,
              log=None,
              proxy=None,
              timeout=None,
              repo=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,
              exists_action=None,
              no_deps=False,
              no_install=False,
              no_download=False,
              install_options=None,
              user=None,
              runas=None,
              no_chown=False,
              cwd=None,
              pre_releases=False,
              __env__='base'):
    '''
    Make sure the package is installed

    name
        The name of the python package to install
    user
        The user under which to run pip
    pip_bin : None
        Deprecated, use bin_env
    env : None
        Deprecated, use bin_env
    bin_env : None
        the pip executable or virtualenv to use

    '''
    if pip_bin and not bin_env:
        bin_env = pip_bin
    elif env and not bin_env:
        bin_env = env

    ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}

    scheme, netloc, path, query, fragment = urlparse.urlsplit(name)
    if scheme and netloc:
        # parse as VCS url
        prefix = path.lstrip('/').split('@', 1)[0]
        if scheme.startswith('git+'):
            prefix = prefix.rstrip('.git')
    else:
        # Split the passed string into the prefix and version
        try:
            version_spec = list(re.match(
                (r'([^=<>]+)(?:(?:([<>]=?|==?)([^<>=,]+))'
                 r'(?:,([<>]=?|==?)([^<>=]+))?)?$'),
                name
            ).groups())
            prefix = version_spec.pop(0)
        except AttributeError:
            ret['result'] = False
            ret['comment'] = 'Invalidly-formatted package {0}'.format(name)
            return ret
        else:
            # Check to see if '=' was used instead of '=='. version_spec will
            # contain two sets of comparison operators and version numbers, so
            # we are checking elements 0 and 2 of this list.
            if any((version_spec[x] == '=' for x in (0, 2))):
                ret['result'] = False
                ret['comment'] = ('Invalid version specification in '
                                  'package {0}. \'=\' is not supported, use '
                                  '\'==\' instead.'.format(name))
                return ret

    if runas is not None:
        # The user is using a deprecated argument, warn!
        msg = (
            'The \'runas\' argument to pip.installed is deprecated, and will '
            'be removed in 0.18.0. Please use \'user\' instead.'
        )
        salt.utils.warn_until((0, 18), msg)
        ret.setdefault('warnings', []).append(msg)

        # "There can only be one"
        if user:
            raise CommandExecutionError(
                'The \'runas\' and \'user\' arguments are mutually exclusive. '
                'Please use \'user\' as \'runas\' is being deprecated.'
            )
        # Support deprecated 'runas' arg
        else:
            user = runas

    try:
        pip_list = __salt__['pip.list'](prefix, bin_env=bin_env,
                                        user=user, cwd=cwd)
        prefix_realname = _find_key(prefix, pip_list)
    except (CommandNotFoundError, CommandExecutionError) as err:
        ret['result'] = False
        ret['comment'] = 'Error installing \'{0}\': {1}'.format(name, err)
        return ret

    if ignore_installed is False and prefix_realname is not None:
        if force_reinstall is False and not upgrade:
            # Check desired version (if any) against currently-installed
            if (
                    any(version_spec) and
                    _fulfills_version_spec(pip_list[prefix_realname],
                                           version_spec)
                    ) or (not any(version_spec)):
                ret['result'] = True
                ret['comment'] = ('Python package {0} already '
                                  'installed'.format(name))
                return ret

    if __opts__['test']:
        ret['result'] = None
        ret['comment'] = \
            'Python package {0} is set to be installed'.format(name)
        return ret

    # Replace commas (used for version ranges) with semicolons (which are not
    # supported) in name so it does not treat them as multiple packages.  Comma
    # will be re-added in pip.install call.
    name = name.replace(',', ';')

    if repo:
        name = repo

    # If a requirements file is specified, only install the contents of the
    # requirements file. Similarly, using the --editable flag with pip should
    # also ignore the "name" parameter.
    if requirements or editable:
        name = ''

    pip_install_call = __salt__['pip.install'](
        pkgs='"{0}"'.format(name),
        requirements=requirements,
        bin_env=bin_env,
        log=log,
        proxy=proxy,
        timeout=timeout,
        editable=editable,
        find_links=find_links,
        index_url=index_url,
        extra_index_url=extra_index_url,
        no_index=no_index,
        mirrors=mirrors,
        build=build,
        target=target,
        download=download,
        download_cache=download_cache,
        source=source,
        upgrade=upgrade,
        force_reinstall=force_reinstall,
        ignore_installed=ignore_installed,
        exists_action=exists_action,
        no_deps=no_deps,
        no_install=no_install,
        no_download=no_download,
        install_options=install_options,
        user=user,
        no_chown=no_chown,
        cwd=cwd,
        pre_releases=pre_releases,
        __env__=__env__
    )

    if pip_install_call and (pip_install_call['retcode'] == 0):
        ret['result'] = True

        pkg_list = __salt__['pip.list'](prefix, bin_env, user=user, cwd=cwd)
        if not pkg_list:
            ret['comment'] = (
                'There was no error installing package \'{0}\' although '
                'it does not show when calling \'pip.freeze\'.'.format(name)
            )
            ret['changes']['{0}==???'.format(name)] = 'Installed'
            return ret

        version = list(pkg_list.values())[0]
        pkg_name = next(iter(pkg_list))
        ret['changes']['{0}=={1}'.format(pkg_name, version)] = 'Installed'
        ret['comment'] = 'Package was successfully installed'
    elif pip_install_call:
        ret['result'] = False
        ret['comment'] = ('Failed to install package {0}. '
                          'Error: {1} {2}').format(name,
                                                   pip_install_call['stdout'],
                                                   pip_install_call['stderr'])
    else:
        ret['result'] = False
        ret['comment'] = 'Could not install package'

    return ret

def removed(name,
            requirements=None,
            bin_env=None,
            log=None,
            proxy=None,
            timeout=None,
            user=None,
            runas=None,
            cwd=None,
            __env__='base'):
    '''
    Make sure that a package is not installed.

    name
        The name of the package to uninstall
    user
        The user under which to run pip
    bin_env : None
        the pip executable or virtualenenv to use
    '''
    ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}

    if runas is not None:
        # The user is using a deprecated argument, warn!
        msg = (
            'The \'runas\' argument to pip.installed is deprecated, and will '
            'be removed in 0.18.0. Please use \'user\' instead.'
        )
        salt.utils.warn_until((0, 18), msg)
        ret.setdefault('warnings', []).append(msg)

    # "There can only be one"
    if runas is not None and user:
        raise CommandExecutionError(
            'The \'runas\' and \'user\' arguments are mutually exclusive. '
            'Please use \'user\' as \'runas\' is being deprecated.'
        )
    # Support deprecated 'runas' arg
    elif runas is not None and not user:
        user = runas

    try:
        pip_list = __salt__['pip.list'](bin_env=bin_env, user=user, cwd=cwd)
    except (CommandExecutionError, CommandNotFoundError) as err:
        ret['result'] = False
        ret['comment'] = 'Error uninstalling \'{0}\': {1}'.format(name, err)
        return ret

    if name not in pip_list:
        ret['result'] = True
        ret['comment'] = 'Package is not installed.'
        return ret

    if __opts__['test']:
        ret['result'] = None
        ret['comment'] = 'Package {0} is set to be removed'.format(name)
        return ret

    if __salt__['pip.uninstall'](pkgs=name,
                                 requirements=requirements,
                                 bin_env=bin_env,
                                 log=log,
                                 proxy=proxy,
                                 timeout=timeout,
                                 user=user,
                                 cwd=cwd,
                                 __env__='base'):
        ret['result'] = True
        ret['changes'][name] = 'Removed'
        ret['comment'] = 'Package was successfully removed.'
    else:
        ret['result'] = False
        ret['comment'] = 'Could not remove package.'
    return ret
<|endoftext|>


<commit_before>'''
Installation of packages using OS package managers such as yum or apt-get.
==========================================================================

Salt can manage software packages via the pkg state module, packages can be
set up to be installed, latest, removed and purged. Package management
declarations are typically rather simple:

.. code-block:: yaml

    vim:
      pkg.installed
'''

# Import python ilbs
import logging
import os
from distutils.version import LooseVersion

# Import salt libs
import salt.utils

logger = logging.getLogger(__name__)

def __gen_rtag():
    '''
    Return the location of the refresh tag
    '''
    return os.path.join(__opts__['cachedir'], 'pkg_refresh')

def installed(
        name,
        version=None,
        refresh=False,
        repo='',
        skip_verify=False,
        **kwargs):
    '''
    Verify that the package is installed, and only that it is installed. This
    state will not upgrade an existing package and only verify that it is
    installed

    name
        The name of the package to install
    repo
        Specify a non-default repository to install from
    skip_verify : False
        Skip the GPG verification check for the package to be installed
    version : None
        Install a specific version of a package

    Usage::

        httpd:
          pkg:
            - installed
            - repo: mycustomrepo
            - skip_verify: True
            - version: 2.0.6~ubuntu3
    '''
    rtag = __gen_rtag()
    cver = __salt__['pkg.version'](name)
    if cver == version:
        # The package is installed and is the correct version
        return {'name': name,
                'changes': {},
                'result': True,
                'comment': ('Package {0} is already installed and is the '
                            'correct version').format(name)}
    elif cver:
        # The package is installed
        return {'name': name,
                'changes': {},
                'result': True,
                'comment': 'Package {0} is already installed'.format(name)}

    if __opts__['test']:
        return {'name': name,
                'changes': {},
                'result': None,
                'comment': 'Package {0} is set to be installed'.format(name)}

    if refresh or os.path.isfile(rtag):
        changes = __salt__['pkg.install'](name,
                          True,
                          version=version,
                          repo=repo,
                          skip_verify=skip_verify,
                          **kwargs)
        if os.path.isfile(rtag):
            os.remove(rtag)
    else:
        changes = __salt__['pkg.install'](name,
                          version=version,
                          repo=repo,
                          skip_verify=skip_verify,
                          **kwargs)
    if not changes:
        return {'name': name,
                'changes': changes,
                'result': False,
                'comment': 'Package {0} failed to install'.format(name)}
    return {'name': name,
            'changes': changes,
            'result': True,
            'comment': 'Package {0} installed'.format(name)}

def latest(name, refresh=False, repo='', skip_verify=False, **kwargs):
    '''
    Verify that the named package is installed and the latest available
    package. If the package can be updated this state function will update
    the package. Generally it is better for the ``installed`` function to be
    used, as ``latest`` will update the package whenever a new package is
    available.

    name
        The name of the package to maintain at the latest available version
    repo : (default)
        Specify a non-default repository to install from
    skip_verify : False
        Skip the GPG verification check for the package to be installed
    '''
    rtag = __gen_rtag()
    ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''}

    version = __salt__['pkg.version'](name)
    avail = __salt__['pkg.available_version'](name)

    if not version:
        # Net yet installed
        has_newer = True
    elif not avail:
        # Already at latest
        has_newer = False
    else:
        try:
            has_newer = LooseVersion(avail) > LooseVersion(version)
        except AttributeError:
            logger.debug(
                    'Error comparing versions for "{0}" ({1} > {2})'.format(
                    name, avail, version)
                    )
            ret['comment'] = 'No version could be retrieved for "{0}"'.format(
                    name)
            return ret

    if has_newer:
        if __opts__['test']:
            ret['result'] = None
            ret['comment'] = 'Package {0} is set to be upgraded'.format(name)
            return ret
        if refresh or os.path.isfile(rtag):
            ret['changes'] = __salt__['pkg.install'](name,
                             True,
                             repo=repo,
                             skip_verify=skip_verify,
                             **kwargs)
            if os.path.isfile(rtag):
                os.remove(rtag)

        else:
            ret['changes'] = __salt__['pkg.install'](name,
                             repo=repo,
                             skip_verify=skip_verify,
                             **kwargs)

        if ret['changes']:
            ret['comment'] = 'Package {0} upgraded to latest'.format(name)
            ret['result'] = True
        else:
            ret['comment'] = 'Package {0} failed to install'.format(name)
            ret['result'] = False
            return ret
    else:
        ret['comment'] = 'Package {0} already at latest'.format(name)
        ret['result'] = True

    return ret

def removed(name):
    '''
    Verify that the package is removed, this will remove the package via
    the remove function in the salt pkg module for the platform.

    name
        The name of the package to be removed
    '''
    changes = {}
    if not __salt__['pkg.version'](name):
        return {'name': name,
                'changes': {},
                'result': True,
                'comment': 'Package {0} is not installed'.format(name)}
    else:
        if __opts__['test']:
            return {'name': name,
                    'changes': {},
                    'result': None,
                    'comment': 'Package {0} is set to be installed'.format(
                        name)}
        changes['removed'] = __salt__['pkg.remove'](name)
    if not changes:
        return {'name': name,
                'changes': changes,
                'result': False,
                'comment': 'Package {0} failed to remove'.format(name)}
    return {'name': name,
        'changes': changes,
        'result': True,
        'comment': 'Package {0} removed'.format(name)}

def purged(name):
    '''
    Verify that the package is purged, this will call the purge function in the
    salt pkg module for the platform.

    name
        The name of the package to be purged
    '''
    changes = {}
    if not __salt__['pkg.version'](name):
        return {'name': name,
                'changes': {},
                'result': True,
                'comment': 'Package {0} is not installed'.format(name)}
    else:
        if __opts__['test']:
            return {'name': name,
                    'changes': {},
                    'result': None,
                    'comment': 'Package {0} is set to be purged'.format(name)}
        changes['removed'] = __salt__['pkg.purge'](name)

    if not changes:
        return {'name': name,
                'changes': changes,
                'result': False,
                'comment': 'Package {0} failed to purge'.format(name)}
    return {'name': name,
        'changes': changes,
        'result': True,
        'comment': 'Package {0} purged'.format(name)}

def mod_init(low):
    '''
    Set a flag to tell the install functions to refresh the package database.
    This ensures that the package database is refreshed only once durring
    a state run significaltly improving the speed of package management
    durring a state run.

    It sets a flag for a number of reasons, primarily due to timeline logic.
    When originally setting up the mod_init for pkg a number of corner cases
    arose with different package managers and how they refresh package data.
    '''
    if low['fun'] == 'installed' or low['fun'] == 'latest':
        rtag = __gen_rtag()
        if not os.path.exists(rtag):
            salt.utils.fopen(rtag, 'w+').write('')
        return True
    return False
<commit_msg>pep8 fix<commit_after>'''
Installation of packages using OS package managers such as yum or apt-get.
==========================================================================

Salt can manage software packages via the pkg state module, packages can be
set up to be installed, latest, removed and purged. Package management
declarations are typically rather simple:

.. code-block:: yaml

    vim:
      pkg.installed
'''

# Import python ilbs
import logging
import os
from distutils.version import LooseVersion

# Import salt libs
import salt.utils

logger = logging.getLogger(__name__)

def __gen_rtag():
    '''
    Return the location of the refresh tag
    '''
    return os.path.join(__opts__['cachedir'], 'pkg_refresh')

def installed(
        name,
        version=None,
        refresh=False,
        repo='',
        skip_verify=False,
        **kwargs):
    '''
    Verify that the package is installed, and only that it is installed. This
    state will not upgrade an existing package and only verify that it is
    installed

    name
        The name of the package to install
    repo
        Specify a non-default repository to install from
    skip_verify : False
        Skip the GPG verification check for the package to be installed
    version : None
        Install a specific version of a package

    Usage::

        httpd:
          pkg:
            - installed
            - repo: mycustomrepo
            - skip_verify: True
            - version: 2.0.6~ubuntu3
    '''
    rtag = __gen_rtag()
    cver = __salt__['pkg.version'](name)
    if cver == version:
        # The package is installed and is the correct version
        return {'name': name,
                'changes': {},
                'result': True,
                'comment': ('Package {0} is already installed and is the '
                            'correct version').format(name)}
    elif cver:
        # The package is installed
        return {'name': name,
                'changes': {},
                'result': True,
                'comment': 'Package {0} is already installed'.format(name)}

    if __opts__['test']:
        return {'name': name,
                'changes': {},
                'result': None,
                'comment': 'Package {0} is set to be installed'.format(name)}

    if refresh or os.path.isfile(rtag):
        changes = __salt__['pkg.install'](name,
                                          refresh=True,
                                          version=version,
                                          repo=repo,
                                          skip_verify=skip_verify,
                                          **kwargs)
        if os.path.isfile(rtag):
            os.remove(rtag)
    else:
        changes = __salt__['pkg.install'](name,
                                          version=version,
                                          repo=repo,
                                          skip_verify=skip_verify,
                                          **kwargs)
    if not changes:
        return {'name': name,
                'changes': changes,
                'result': False,
                'comment': 'Package {0} failed to install'.format(name)}
    return {'name': name,
            'changes': changes,
            'result': True,
            'comment': 'Package {0} installed'.format(name)}

def latest(name, refresh=False, repo='', skip_verify=False, **kwargs):
    '''
    Verify that the named package is installed and the latest available
    package. If the package can be updated this state function will update
    the package. Generally it is better for the ``installed`` function to be
    used, as ``latest`` will update the package whenever a new package is
    available.

    name
        The name of the package to maintain at the latest available version
    repo : (default)
        Specify a non-default repository to install from
    skip_verify : False
        Skip the GPG verification check for the package to be installed
    '''
    rtag = __gen_rtag()
    ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''}

    version = __salt__['pkg.version'](name)
    avail = __salt__['pkg.available_version'](name)

    if not version:
        # Net yet installed
        has_newer = True
    elif not avail:
        # Already at latest
        has_newer = False
    else:
        try:
            has_newer = LooseVersion(avail) > LooseVersion(version)
        except AttributeError:
            logger.debug('Error comparing versions'
                         ' for "{0}" ({1} > {2})'.format(name,
                                                         avail,
                                                         version)
                         )
            ret['comment'] = 'No version could be retrieved' \
                             ' for "{0}"'.format(name)
            return ret

    if has_newer:
        if __opts__['test']:
            ret['result'] = None
            ret['comment'] = 'Package {0} is set to be upgraded'.format(name)
            return ret
        if refresh or os.path.isfile(rtag):
            ret['changes'] = __salt__['pkg.install'](name,
                                                     refresh=True,
                                                     repo=repo,
                                                     skip_verify=skip_verify,
                                                     **kwargs)
            if os.path.isfile(rtag):
                os.remove(rtag)

        else:
            ret['changes'] = __salt__['pkg.install'](name,
                                                     repo=repo,
                                                     skip_verify=skip_verify,
                                                     **kwargs)

        if ret['changes']:
            ret['comment'] = 'Package {0} upgraded to latest'.format(name)
            ret['result'] = True
        else:
            ret['comment'] = 'Package {0} failed to install'.format(name)
            ret['result'] = False
            return ret
    else:
        ret['comment'] = 'Package {0} already at latest'.format(name)
        ret['result'] = True

    return ret

def removed(name):
    '''
    Verify that the package is removed, this will remove the package via
    the remove function in the salt pkg module for the platform.

    name
        The name of the package to be removed
    '''
    changes = {}
    if not __salt__['pkg.version'](name):
        return {'name': name,
                'changes': {},
                'result': True,
                'comment': 'Package {0} is not installed'.format(name)}
    else:
        if __opts__['test']:
            return {'name': name,
                    'changes': {},
                    'result': None,
                    'comment': 'Package {0} is set to be installed'.format(
                        name)}
        changes['removed'] = __salt__['pkg.remove'](name)
    if not changes:
        return {'name': name,
                'changes': changes,
                'result': False,
                'comment': 'Package {0} failed to remove'.format(name)}
    return {'name': name,
            'changes': changes,
            'result': True,
            'comment': 'Package {0} removed'.format(name)}

def purged(name):
    '''
    Verify that the package is purged, this will call the purge function in the
    salt pkg module for the platform.

    name
        The name of the package to be purged
    '''
    changes = {}
    if not __salt__['pkg.version'](name):
        return {'name': name,
                'changes': {},
                'result': True,
                'comment': 'Package {0} is not installed'.format(name)}
    else:
        if __opts__['test']:
            return {'name': name,
                    'changes': {},
                    'result': None,
                    'comment': 'Package {0} is set to be purged'.format(name)}
        changes['removed'] = __salt__['pkg.purge'](name)

    if not changes:
        return {'name': name,
                'changes': changes,
                'result': False,
                'comment': 'Package {0} failed to purge'.format(name)}
    return {'name': name,
            'changes': changes,
            'result': True,
            'comment': 'Package {0} purged'.format(name)}

def mod_init(low):
    '''
    Set a flag to tell the install functions to refresh the package database.
    This ensures that the package database is refreshed only once durring
    a state run significaltly improving the speed of package management
    durring a state run.

    It sets a flag for a number of reasons, primarily due to timeline logic.
    When originally setting up the mod_init for pkg a number of corner cases
    arose with different package managers and how they refresh package data.
    '''
    if low['fun'] == 'installed' or low['fun'] == 'latest':
        rtag = __gen_rtag()
        if not os.path.exists(rtag):
            salt.utils.fopen(rtag, 'w+').write('')
        return True
    return False
<|endoftext|>


<commit_before>from intermine.webservice import Service
import itertools

def lines_of(x):
    return chunker(0, x)

def chunker(i, x):
    d = {"accum": i}

    def func(y):
        i = d["accum"]
        grp = i / x
        i += 1
        d["accum"] = i
        return grp
    return func

col_width = 15
cols = 8
sep = '| '
ellipsis = '...'
line_width = col_width * cols + (cols - 1) * len(sep)

def fit_to_cell(a): return a.ljust(col_width) if len(
    a) <= col_width else a[:col_width - len(ellipsis)] + ellipsis

hrule = "-" * line_width
summary = "\n%s: %d Alleles"

s = Service("www.flymine.org/query")

Gene = s.model.Gene

q = s.query(Gene).\
      add_columns("name", "symbol", "alleles.*").\
      filter(Gene.symbol == ["zen", "eve", "bib", "h"]).\
      filter(Gene.alleles.symbol == "*hs*").\
      outerjoin(Gene.alleles).\
      order_by("symbol")

for row in q.rows():
    print row

for gene in s.query(s.model.Gene).filter(s.model.Gene.symbol ==
         ["zen", "eve", "bib", "h"]).add_columns(s.model.Gene.alleles):

    print summary % (gene.symbol, len(gene.alleles))
    print hrule

    for k, line_of_alleles in itertools.groupby(sorted(map(lambda a: a.symbol,
            gene.alleles)), lines_of(cols)):
        print sep.join(map(fit_to_cell, line_of_alleles))

    print "\nAllele Classes:"
    allele_classes = [(key, len(list(group))) for
                      key, group in itertools.groupby(
        sorted(map(lambda x: x.alleleClass, gene.alleles)))]
    for pair in reversed(sorted(allele_classes, key=lambda g: g[1])):
        print "%s (%d)" % pair

    print hrule
<commit_msg>PEP8 Update<commit_after>from intermine.webservice import Service
import itertools

def lines_of(x):
    return chunker(0, x)

def chunker(i, x):
    d = {"accum": i}

    def func(y):
        i = d["accum"]
        grp = i / x
        i += 1
        d["accum"] = i
        return grp
    return func

col_width = 15
cols = 8
sep = '| '
ellipsis = '...'
line_width = col_width * cols + (cols - 1) * len(sep)

def fit_to_cell(a): return a.ljust(col_width) if len(
    a) <= col_width else a[:col_width - len(ellipsis)] + ellipsis

hrule = "-" * line_width
summary = "\n%s: %d Alleles"

s = Service("www.flymine.org/query")

Gene = s.model.Gene

q = s.query(Gene).\
      add_columns("name", "symbol", "alleles.*").\
      filter(Gene.symbol == ["zen", "eve", "bib", "h"]).\
      filter(Gene.alleles.symbol == "*hs*").\
      outerjoin(Gene.alleles).\
      order_by("symbol")

for row in q.rows():
    print row

for gene in s.query(s.model.Gene).filter(s.model.Gene.symbol ==
            ["zen", "eve", "bib", "h"]).add_columns(s.model.Gene.alleles):

    print summary % (gene.symbol, len(gene.alleles))
    print hrule

    for k, line_of_alleles in itertools.groupby(sorted(map(lambda a: a.symbol,
            gene.alleles)), lines_of(cols)):
        print sep.join(map(fit_to_cell, line_of_alleles))

    print "\nAllele Classes:"
    allele_classes = [(key, len(list(group))) for
                      key, group in itertools.groupby(
        sorted(map(lambda x: x.alleleClass, gene.alleles)))]
    for pair in reversed(sorted(allele_classes, key=lambda g: g[1])):
        print "%s (%d)" % pair

    print hrule
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
/***************************************************************************
 Svir
                                 A QGIS plugin
 OpenQuake Social Vulnerability and Integrated Risk
                              -------------------
        begin                : 2013-10-24
        copyright            : (C) 2013 by GEM Foundation
        email                : devops@openquake.org
 ***************************************************************************/

# Copyright (c) 2013-2014, GEM Foundation.
#
# OpenQuake is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenQuake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake.  If not, see <http://www.gnu.org/licenses/>.
"""
from PyQt4.QtCore import pyqtSlot, Qt
from PyQt4.QtGui import (QDialog,
                         QDialogButtonBox)

from ui.ui_select_sv_variables import Ui_SelectSvVariablesDialog
from import_sv_data import SvDownloadError
from utils import WaitCursorManager

class SelectSvVariablesDialog(QDialog):
    """
    Modal dialog giving to the user the possibility to select
    social vulnerability variables to import from the oq-platform
    """
    def __init__(self, downloader):
        QDialog.__init__(self)
        # Set up the user interface from Designer.
        self.ui = Ui_SelectSvVariablesDialog()
        self.ui.setupUi(self)
        self.ok_button = self.ui.buttonBox.button(QDialogButtonBox.Ok)
        self.set_ok_button()
        # login to platform, to be able to retrieve sv indices
        self.sv_downloader = downloader
        self.indicators_info_dict = {}
        with WaitCursorManager():
            self.fill_themes()

    @pyqtSlot(str)
    def on_theme_cbx_currentIndexChanged(self):
        theme = self.ui.theme_cbx.currentText()
        with WaitCursorManager():
            self.fill_subthemes(theme)

    @pyqtSlot()
    def on_filter_btn_clicked(self):
        with WaitCursorManager():
            self.fill_names()

    @pyqtSlot()
    def on_add_name_btn_clicked(self):
        theme = self.ui.theme_cbx.currentText()
        name = self.ui.name_cbx.currentText()
        name = '%s,%s' % (theme, name)
        # if not self.ui.selected_names_lst.findItems(
        #         name, Qt.MatchFixedString):
        #         self.ui.selected_names_lst.addItem(name)
        #         self.set_ok_button()

    @pyqtSlot()
    def on_remove_name_btn_clicked(self):
        pass
        # row = self.ui.selected_names_lst.currentRow()
        # if row is not None:
        #     self.ui.selected_names_lst.takeItem(row)
        #     self.set_ok_button()

    @pyqtSlot()
    def on_clear_btn_clicked(self):
        pass
        # self.ui.selected_names_lst.clear()
        # self.set_ok_button()

    def set_ok_button(self):
        pass
        # self.ok_button.setEnabled(self.ui.selected_names_lst.count() > 0)

    def fill_themes(self):
        self.ui.theme_cbx.clear()
        # load list of themes from the platform
        self.ui.theme_cbx.addItems([None])
        try:
            themes = self.sv_downloader.get_themes()
            self.ui.theme_cbx.addItems(themes)
        except SvDownloadError as e:
            raise SvDownloadError(
                "Unable to download social vulnerability themes: %s" % e)
        # populate the subsequent combo boxes accordingly with the currently
        # selected item
        current_theme = self.ui.theme_cbx.currentText()
        self.fill_subthemes(current_theme)

    def fill_subthemes(self, theme):
        self.ui.subtheme_cbx.clear()
        # load list of subthemes from the platform
        self.ui.subtheme_cbx.addItems([None])
        try:
            subthemes = self.sv_downloader.get_subthemes_by_theme(theme)
            self.ui.subtheme_cbx.addItems(subthemes)
        except SvDownloadError as e:
            raise SvDownloadError(
                "Unable to download social vulnerability subthemes: %s" % e)

    def fill_names(self):
        self.ui.list_multiselect.set_unselected_items([])
        # load list of social vulnerability variable names from the platform

        name_filter = self.ui.name_filter_le.text()
        keywords = self.ui.keywords_le.text()
        theme = self.ui.theme_cbx.currentText()
        subtheme = self.ui.subtheme_cbx.currentText()
        try:
            self.indicators_info_dict = \
                self.sv_downloader.get_indicators_info(name_filter,
                                                       keywords,
                                                       theme,
                                                       subtheme)
            names = sorted(
                [code + ': ' + self.indicators_info_dict[code]['name']
                    for code in self.indicators_info_dict])
            self.ui.list_multiselect.add_unselected_items(names)
            self.ui.list_multiselect.unselected_widget.currentItemChanged.connect(
                self.update_hint)
            self.ui.list_multiselect.selected_widget.currentItemChanged.connect(
                self.update_hint)
        except SvDownloadError as e:
            raise SvDownloadError(
                "Unable to download social vulnerability names: %s" % e)

    def update_hint(self, current, previous):
        hint_text = current.text()
        indicator_code = current.text().split(':')[0]
        indicator_info_dict = self.indicators_info_dict[indicator_code]
        hint_text += '\n\n' + 'Description:\n' + indicator_info_dict[
            'description']
        hint_text += '\n\n' + 'Source:\n' + indicator_info_dict['source']
        hint_text += '\n\n' + 'Aggregation method:\n' + indicator_info_dict[
            'aggregation_method']
        # for key in indicator_info_dict:
        #     hint_text += key.upper() + ': ' + indicator_info_dict[key] + '\n'
        self.ui.indicator_details.setText(hint_text)
<commit_msg>Don't update hint if no current selection is available<commit_after># -*- coding: utf-8 -*-
"""
/***************************************************************************
 Svir
                                 A QGIS plugin
 OpenQuake Social Vulnerability and Integrated Risk
                              -------------------
        begin                : 2013-10-24
        copyright            : (C) 2013 by GEM Foundation
        email                : devops@openquake.org
 ***************************************************************************/

# Copyright (c) 2013-2014, GEM Foundation.
#
# OpenQuake is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenQuake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake.  If not, see <http://www.gnu.org/licenses/>.
"""
from PyQt4.QtCore import pyqtSlot, Qt
from PyQt4.QtGui import (QDialog,
                         QDialogButtonBox)

from ui.ui_select_sv_variables import Ui_SelectSvVariablesDialog
from import_sv_data import SvDownloadError
from utils import WaitCursorManager

class SelectSvVariablesDialog(QDialog):
    """
    Modal dialog giving to the user the possibility to select
    social vulnerability variables to import from the oq-platform
    """
    def __init__(self, downloader):
        QDialog.__init__(self)
        # Set up the user interface from Designer.
        self.ui = Ui_SelectSvVariablesDialog()
        self.ui.setupUi(self)
        self.ok_button = self.ui.buttonBox.button(QDialogButtonBox.Ok)
        self.set_ok_button()
        # login to platform, to be able to retrieve sv indices
        self.sv_downloader = downloader
        self.indicators_info_dict = {}
        with WaitCursorManager():
            self.fill_themes()
        self.ui.list_multiselect.unselected_widget.currentItemChanged.connect(
            self.update_hint)
        self.ui.list_multiselect.selected_widget.currentItemChanged.connect(
            self.update_hint)

    @pyqtSlot(str)
    def on_theme_cbx_currentIndexChanged(self):
        theme = self.ui.theme_cbx.currentText()
        with WaitCursorManager():
            self.fill_subthemes(theme)

    @pyqtSlot()
    def on_filter_btn_clicked(self):
        with WaitCursorManager():
            self.fill_names()

    @pyqtSlot()
    def on_add_name_btn_clicked(self):
        theme = self.ui.theme_cbx.currentText()
        name = self.ui.name_cbx.currentText()
        name = '%s,%s' % (theme, name)
        # if not self.ui.selected_names_lst.findItems(
        #         name, Qt.MatchFixedString):
        #         self.ui.selected_names_lst.addItem(name)
        #         self.set_ok_button()

    @pyqtSlot()
    def on_remove_name_btn_clicked(self):
        pass
        # row = self.ui.selected_names_lst.currentRow()
        # if row is not None:
        #     self.ui.selected_names_lst.takeItem(row)
        #     self.set_ok_button()

    @pyqtSlot()
    def on_clear_btn_clicked(self):
        pass
        # self.ui.selected_names_lst.clear()
        # self.set_ok_button()

    def set_ok_button(self):
        pass
        # self.ok_button.setEnabled(self.ui.selected_names_lst.count() > 0)

    def fill_themes(self):
        self.ui.theme_cbx.clear()
        # load list of themes from the platform
        self.ui.theme_cbx.addItems([None])
        try:
            themes = self.sv_downloader.get_themes()
            self.ui.theme_cbx.addItems(themes)
        except SvDownloadError as e:
            raise SvDownloadError(
                "Unable to download social vulnerability themes: %s" % e)
        # populate the subsequent combo boxes accordingly with the currently
        # selected item
        current_theme = self.ui.theme_cbx.currentText()
        self.fill_subthemes(current_theme)

    def fill_subthemes(self, theme):
        self.ui.subtheme_cbx.clear()
        # load list of subthemes from the platform
        self.ui.subtheme_cbx.addItems([None])
        try:
            subthemes = self.sv_downloader.get_subthemes_by_theme(theme)
            self.ui.subtheme_cbx.addItems(subthemes)
        except SvDownloadError as e:
            raise SvDownloadError(
                "Unable to download social vulnerability subthemes: %s" % e)

    def fill_names(self):
        self.ui.list_multiselect.set_unselected_items([])
        # load list of social vulnerability variable names from the platform

        name_filter = self.ui.name_filter_le.text()
        keywords = self.ui.keywords_le.text()
        theme = self.ui.theme_cbx.currentText()
        subtheme = self.ui.subtheme_cbx.currentText()
        try:
            self.indicators_info_dict = \
                self.sv_downloader.get_indicators_info(name_filter,
                                                       keywords,
                                                       theme,
                                                       subtheme)
            names = sorted(
                [code + ': ' + self.indicators_info_dict[code]['name']
                    for code in self.indicators_info_dict])
            self.ui.list_multiselect.add_unselected_items(names)
        except SvDownloadError as e:
            raise SvDownloadError(
                "Unable to download social vulnerability names: %s" % e)

    def update_hint(self, current, previous):
        if not current:
            return
        hint_text = current.text()
        indicator_code = current.text().split(':')[0]
        indicator_info_dict = self.indicators_info_dict[indicator_code]
        hint_text += '\n\n' + 'Description:\n' + indicator_info_dict[
            'description']
        hint_text += '\n\n' + 'Source:\n' + indicator_info_dict['source']
        hint_text += '\n\n' + 'Aggregation method:\n' + indicator_info_dict[
            'aggregation_method']
        # for key in indicator_info_dict:
        #     hint_text += key.upper() + ': ' + indicator_info_dict[key] + '\n'
        self.ui.indicator_details.setText(hint_text)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import encodings
import json
from os.path import expanduser
import re

import oursql

from .user import User
from .util import to_wiki_format
from .wikidata import Wikidata
from .wikiproject import WikiProject

__all__ = ["Bot"]

class Bot:
    """Represents an instance of the Reports bot on a particular wiki.

    An instance of this class is accessible from all tasks as their '_bot'
    attribute, and is their primary way of interacting with the external world.
    It provides access to databases and structured representations of many
    objects like WikiProjects and users.
    """

    def __init__(self, config, project, lang):
        self._config = config
        self._project = project
        self._lang = lang

        self._site = None
        self._wikidb = None
        self._localdb = None
        self._wikidata = None
        self._project_config = None

    @staticmethod
    def _register_utf8mb4():
        """Install utf8mb4 as a valid alias for utf_8."""
        if "utf8mb4" not in encodings.aliases.aliases:
            encodings.aliases.aliases["utf8mb4"] = "utf_8"
        if hasattr(encodings, "_cache") and "utf8mb4" in encodings._cache:
            del encodings._cache["utf8mb4"]

    def _sql_connect(self, **kwargs):
        """Return a new SQL connection using the given arguments.

        We apply some transformations: a default file is configured if not
        username or password is provided, a default charset is set, and
        autocommit is turned off.

        The default charset really should be utf8mb4, but because of a bug in
        oursql's handling of charsets (conflating Python codecs with MySQL
        charsets, which are sometimes different), we cannot. Instead, if
        utf8mb4 is chosen, we'll install it as an alias first.
        """
        if ("read_default_file" not in kwargs and "user" not in kwargs
                and "password" not in kwargs):
            kwargs["read_default_file"] = expanduser("~/.my.cnf")
        if "charset" not in kwargs:
            kwargs["charset"] = "utf8mb4"
        if "autoping" not in kwargs:
            kwargs["autoping"] = True
        if "autoreconnect" not in kwargs:
            kwargs["autoreconnect"] = True

        if kwargs["charset"] == "utf8mb4":
            self._register_utf8mb4()

        return oursql.connect(**kwargs)

    def _load_project_config(self):
        """Load the project config JSON blob from the database.

        After calling this function, the config is available from and cached in
        self._project_config.
        """
        if self._project_config is not None:
            return

        query = "SELECT config_json FROM project_config WHERE config_site = ?"
        with self.localdb as cursor:
            cursor.execute(query, (self.wikiid,))
            results = cursor.fetchall()
            if not results:
                self._project_config = {"defaults": {}, "projects": {}}
                return
            raw = json.loads(results[0][0])

        config = {"defaults": raw["defaults"], "projects": {}}
        for project in raw["projects"]:
            config["projects"][project["name"]] = project
        self._project_config = config

    def _get_project_config(self, name):
        """Return the on-wiki JSON configuration for the given project.

        Default values are automatically resolved. If the project doesn't
        exist, None is returned.
        """
        self._load_project_config()

        name = to_wiki_format(self.site, name)
        if name not in self._project_config["projects"]:
            return None

        config = self._project_config["defaults"].copy()
        config.update(self._project_config["projects"][name])
        return config

    @property
    def config(self):
        """Return the bot's Config object."""
        return self._config

    @property
    def wikiid(self):
        """Return the site's ID; e.g. "enwiki" from "en" and "wikipedia"."""
        # TODO: This is somewhat hacky; should really be using the API here...
        if self._project == "wikipedia":
            res = self._lang + "wiki"
        else:
            res = self._lang + self._project
        return re.sub(r"[^a-zA-Z0-9_-]", "", res).replace("-", "_")

    @property
    def site(self):
        """Return a Pywikibot Site instance."""
        import pywikibot
        if not self._site:
            self._site = pywikibot.Site(self._lang, self._project,
                                        self._config.username)
        return self._site

    @property
    def wikidb(self):
        """Return a connection to the wiki replica SQL database."""
        if not self._wikidb:
            kwargs = self._config.get_wiki_sql(self.wikiid)
            self._wikidb = self._sql_connect(**kwargs)
        return self._wikidb

    @property
    def localdb(self):
        """Return a connection to the local Reports bot/WPX SQL database."""
        if not self._localdb:
            self._localdb = self._sql_connect(**self._config.get_local_sql())
        return self._localdb

    @property
    def wikidata(self):
        """Return an interface to Wikidata."""
        if not self._wikidata:
            sql_kwargs = self._config.get_wiki_sql("wikidatawiki")
            sql_conn = self._sql_connect(**sql_kwargs)
            self._wikidata = Wikidata(sql_conn)
        return self._wikidata

    def get_page(self, title):
        """Return a Pywikibot Page instance for the given page."""
        import pywikibot
        return pywikibot.Page(self.site, title)

    def get_project(self, name):
        """Return a WikiProject object corresponding to the given name.

        The name is the page title of the project's base page, including the
        namespace.
        """
        return WikiProject(self, name, self._get_project_config(name))

    def get_user(self, name):
        """Return a User object corresponding to the given username."""
        return User(self, name)

    def get_configured_projects(self):
        """Return a list of all WikiProjects that are configured."""
        self._load_project_config()
        projects = self._project_config["projects"]
        return [self.get_project(name) for name in projects]
<commit_msg>Add methods for managing last update timers.<commit_after># -*- coding: utf-8 -*-

from datetime import datetime
import encodings
import json
from os.path import expanduser
import re

import oursql

from .user import User
from .util import to_wiki_format
from .wikidata import Wikidata
from .wikiproject import WikiProject

__all__ = ["Bot"]

class Bot:
    """Represents an instance of the Reports bot on a particular wiki.

    An instance of this class is accessible from all tasks as their '_bot'
    attribute, and is their primary way of interacting with the external world.
    It provides access to databases and structured representations of many
    objects like WikiProjects and users.
    """

    def __init__(self, config, project, lang):
        self._config = config
        self._project = project
        self._lang = lang

        self._site = None
        self._wikidb = None
        self._localdb = None
        self._wikidata = None
        self._project_config = None

    @staticmethod
    def _register_utf8mb4():
        """Install utf8mb4 as a valid alias for utf_8."""
        if "utf8mb4" not in encodings.aliases.aliases:
            encodings.aliases.aliases["utf8mb4"] = "utf_8"
        if hasattr(encodings, "_cache") and "utf8mb4" in encodings._cache:
            del encodings._cache["utf8mb4"]

    def _sql_connect(self, **kwargs):
        """Return a new SQL connection using the given arguments.

        We apply some transformations: a default file is configured if not
        username or password is provided, a default charset is set, and
        autocommit is turned off.

        The default charset really should be utf8mb4, but because of a bug in
        oursql's handling of charsets (conflating Python codecs with MySQL
        charsets, which are sometimes different), we cannot. Instead, if
        utf8mb4 is chosen, we'll install it as an alias first.
        """
        if ("read_default_file" not in kwargs and "user" not in kwargs
                and "password" not in kwargs):
            kwargs["read_default_file"] = expanduser("~/.my.cnf")
        if "charset" not in kwargs:
            kwargs["charset"] = "utf8mb4"
        if "autoping" not in kwargs:
            kwargs["autoping"] = True
        if "autoreconnect" not in kwargs:
            kwargs["autoreconnect"] = True

        if kwargs["charset"] == "utf8mb4":
            self._register_utf8mb4()

        return oursql.connect(**kwargs)

    def _load_project_config(self):
        """Load the project config JSON blob from the database.

        After calling this function, the config is available from and cached in
        self._project_config.
        """
        if self._project_config is not None:
            return

        query = "SELECT config_json FROM project_config WHERE config_site = ?"
        with self.localdb as cursor:
            cursor.execute(query, (self.wikiid,))
            results = cursor.fetchall()
            if not results:
                self._project_config = {"defaults": {}, "projects": {}}
                return
            raw = json.loads(results[0][0])

        config = {"defaults": raw["defaults"], "projects": {}}
        for project in raw["projects"]:
            config["projects"][project["name"]] = project
        self._project_config = config

    def _get_project_config(self, name):
        """Return the on-wiki JSON configuration for the given project.

        Default values are automatically resolved. If the project doesn't
        exist, None is returned.
        """
        self._load_project_config()

        name = to_wiki_format(self.site, name)
        if name not in self._project_config["projects"]:
            return None

        config = self._project_config["defaults"].copy()
        config.update(self._project_config["projects"][name])
        return config

    @property
    def config(self):
        """Return the bot's Config object."""
        return self._config

    @property
    def wikiid(self):
        """Return the site's ID; e.g. "enwiki" from "en" and "wikipedia"."""
        # TODO: This is somewhat hacky; should really be using the API here...
        if self._project == "wikipedia":
            res = self._lang + "wiki"
        else:
            res = self._lang + self._project
        return re.sub(r"[^a-zA-Z0-9_-]", "", res).replace("-", "_")

    @property
    def site(self):
        """Return a Pywikibot Site instance."""
        import pywikibot
        if not self._site:
            self._site = pywikibot.Site(self._lang, self._project,
                                        self._config.username)
        return self._site

    @property
    def wikidb(self):
        """Return a connection to the wiki replica SQL database."""
        if not self._wikidb:
            kwargs = self._config.get_wiki_sql(self.wikiid)
            self._wikidb = self._sql_connect(**kwargs)
        return self._wikidb

    @property
    def localdb(self):
        """Return a connection to the local Reports bot/WPX SQL database."""
        if not self._localdb:
            self._localdb = self._sql_connect(**self._config.get_local_sql())
        return self._localdb

    @property
    def wikidata(self):
        """Return an interface to Wikidata."""
        if not self._wikidata:
            sql_kwargs = self._config.get_wiki_sql("wikidatawiki")
            sql_conn = self._sql_connect(**sql_kwargs)
            self._wikidata = Wikidata(sql_conn)
        return self._wikidata

    def get_page(self, title):
        """Return a Pywikibot Page instance for the given page."""
        import pywikibot
        return pywikibot.Page(self.site, title)

    def get_project(self, name):
        """Return a WikiProject object corresponding to the given name.

        The name is the page title of the project's base page, including the
        namespace.
        """
        return WikiProject(self, name, self._get_project_config(name))

    def get_user(self, name):
        """Return a User object corresponding to the given username."""
        return User(self, name)

    def get_configured_projects(self):
        """Return a list of all WikiProjects that are configured."""
        self._load_project_config()
        projects = self._project_config["projects"]
        return [self.get_project(name) for name in projects]

    def get_last_updated(self, key):
        """Get the last update timestamp for the given key."""
        query = """SELECT lu_timestamp
            FROM last_update
            WHERE lu_site = ? AND lu_key = ?"""

        with self.localdb as cursor:
            cursor.execute(query, (self.wikiid, key))
            results = cursor.fetchall()

        if results:
            return results[0][0]
        return datetime.min

    def set_last_updated(self, key, timestamp=None):
        """Set the last update timestamp for the given key."""
        query = """INSERT INTO last_update (lu_site, lu_key, lu_timestamp)
            VALUES(?, ?, ?)
            ON DUPLICATE KEY UPDATE lu_timestamp = ?"""

        if not timestamp:
            timestamp = datetime.utcnow()

        with self.localdb as cursor:
            cursor.execute(query, (self.wikiid, key, timestamp, timestamp))
<|endoftext|>


<commit_before>#!/usr/bin/env python3

import sys
import time
import array
import argparse
import datetime
import requests
import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

# Parse arguments
parser = argparse.ArgumentParser(
        description='Plots of graph of requests to the PPP')
parser.add_argument('outputfile')
parser.add_argument('-g', '--granulometry', type=int,
        default=1, help='Number of sections per hour.')
parser.add_argument('-n', '--nb-hours', type=int,
        default=48, help='Number of sections per hour.')
parser.add_argument('-t', '--ticks', type=int,
        default=6, help='Number of hours between two labels.')
parser.add_argument('-l', '--logger-url', type=str,
        default='http://logger.frontend.askplatyp.us/',
        help='The URL to the logger.')
args = parser.parse_args()
OUTPUT_FILE = args.outputfile
NB_HOURS = args.nb_hours
GRANULOMETRY = 60//args.granulometry
TICKS = args.ticks
LOGGER_URL = args.logger_url

# Initialize matplotlib
fig, ax = plt.subplots()

# Get data
data = requests.get(LOGGER_URL, params={'limit': 10000}).json()

# Convert to datetime
data = [datetime.datetime(*time.strptime(x[1].split('.')[0], "%Y-%m-%d %H:%M:%S")[:6]) for x in data]

# Compute the difference
datemax = datetime.datetime.now()
data = [datemax - x for x in data]
delta = datetime.timedelta(hours=NB_HOURS)
datemin = datemax - delta
ax.set_xlim(datemin, datemax)

# Shrink and convert to minutes
data = [x.seconds//(60*GRANULOMETRY) for x in data if x <= delta]

# Compute the height of the bars
requests_per_minute = array.array('I', (0 for x in range(0, 60*NB_HOURS//GRANULOMETRY)))
for x in data:
    requests_per_minute[(60*NB_HOURS//GRANULOMETRY) - x - 1] += 1

# Units
hours    = mdates.HourLocator(interval=TICKS) # Show ticks every 6 hour
hoursFmt = mdates.DateFormatter('%H:00')
ax.xaxis.set_major_locator(hours)
ax.xaxis.set_major_formatter(hoursFmt)

# Final plot
x = list(map(lambda x:datemin + datetime.timedelta(minutes=x*GRANULOMETRY), range(0, 60*NB_HOURS//GRANULOMETRY)))
ax.plot(x, requests_per_minute, label=None)

# Titles
plt.title("Requests to the PPP in the last %d hours" % NB_HOURS)
plt.xlabel("Time (hours)")
plt.ylabel("Requests (per slice of %d minutes)" % GRANULOMETRY)
#plt.legend()
plt.savefig(OUTPUT_FILE)
<commit_msg>Allow more intervals and granulometries (third item of GH-2).<commit_after>#!/usr/bin/env python3

import sys
import time
import array
import argparse
import datetime
import requests
import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

# From https://github.com/ProgVal/Limnoria/blob/master/plugins/Time/plugin.py
def human_to_seconds(string):
    seconds = 0
    for arg in string.split(' '):
        if not arg or arg[-1] not in 'ywdhms':
            raise ValueError(arg)
        (s, kind) = arg[:-1], arg[-1]
        i = int(s)
        if kind == 'y':
            seconds += i*31536000
        elif kind == 'w':
            seconds += i*604800
        elif kind == 'd':
            seconds += i*86400
        elif kind == 'h':
            seconds += i*3600
        elif kind == 'm':
            seconds += i*60
        elif kind == 's':
            seconds += i
    return seconds

# Parse arguments
parser = argparse.ArgumentParser(
        description='Plots of graph of requests to the PPP')
parser.add_argument('outputfile')
parser.add_argument('-g', '--granulometry', type=str,
        default='1h', help='Number of sections per hour.')
parser.add_argument('-i', '--interval', type=str,
        default='48h', help='Number of sections per hour.')
parser.add_argument('-t', '--ticks', type=int,
        default=6, help='Number of hours between two labels.')
parser.add_argument('-l', '--logger-url', type=str,
        default='http://logger.frontend.askplatyp.us/',
        help='The URL to the logger.')
args = parser.parse_args()
OUTPUT_FILE = args.outputfile
INTERVAL = human_to_seconds(args.interval)
GRANULOMETRY = human_to_seconds(args.granulometry)
TICKS = args.ticks
LOGGER_URL = args.logger_url

# Initialize matplotlib
fig, ax = plt.subplots()

# Get data
data = requests.get(LOGGER_URL, params={'limit': 10000}).json()

# Convert to datetime
data = [datetime.datetime(*time.strptime(x[1].split('.')[0], "%Y-%m-%d %H:%M:%S")[:6]) for x in data]

# Compute the difference
datemax = datetime.datetime.now()
data = [datemax - x for x in data]
delta = datetime.timedelta(seconds=INTERVAL)
datemin = datemax - delta
ax.set_xlim(datemin, datemax)

# Shrink and convert to seconds
data = [x.seconds//GRANULOMETRY for x in data if x <= delta]

# Compute the height of the bars
requests_per_slice = array.array('I', (0 for x in range(0, INTERVAL//GRANULOMETRY)))
for x in data:
    requests_per_slice[(INTERVAL//GRANULOMETRY) - x - 1] += 1

# Units
if INTERVAL < human_to_seconds('6h'):
    locator = mdates.MinuteLocator(interval=TICKS)
    fmt = mdates.DateFormatter('%H:%M')
elif INTERVAL < human_to_seconds('3d'):
    locator = mdates.HourLocator(interval=TICKS)
    fmt = mdates.DateFormatter('%H:00')
elif INTERVAL < human_to_seconds('8w'):
    locator = mdates.DayLocator(interval=TICKS)
    fmt = mdates.DateFormatter('%d-%m')
else:
    raise ValueError('Too large.')
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(fmt)

# Final plot
x = list(map(lambda x:datemin + datetime.timedelta(seconds=x*GRANULOMETRY), range(0, INTERVAL//GRANULOMETRY)))
ax.plot(x, requests_per_slice, label=None)

# Titles
plt.title("Requests to the PPP in the last %d seconds" % INTERVAL) # TODO: readability
plt.xlabel("Time (hours)")
plt.ylabel("Requests (per slice of %d seconds)" % GRANULOMETRY) # TODO: readability
#plt.legend()
plt.savefig(OUTPUT_FILE)
<|endoftext|>


<commit_before>from sympy import symbols, sin, exp, cos, Derivative, Integral, Basic, \
    count_ops, S, And, I, pi, Eq, Or, Not, Xor ,Nand ,Nor, Implies,Equivalent, ITE

x, y, z = symbols('x,y,z')

def test_count_ops_non_visual():
    def count(val):
        return count_ops(val, visual=False)
    assert count(x) == 0
    assert count(x) is not S.Zero
    assert count(x + y) == 1
    assert count(x + y) is not S.One
    assert count(x + y*x + 2*y) == 4
    assert count({x + y: x}) == 1
    assert count({x + y: S(2) + x}) is not S.One
    assert count(Or(x,y)) == 1
    assert count(And(x,y)) == 1
    assert count(Not(x)) == 0
    assert count(Nor(x,y)) == 1
    assert count(Nand(x,y)) == 1
    assert count(Xor(x,y)) == 3
    assert count(Implies(x,y)) == 1
    assert count(Equivalent(x,y)) == 1
    assert count(ITE(x,y,z)) == 3

def test_count_ops_visual():
    ADD, MUL, POW, SIN, COS, EXP, AND, D, G = symbols(
        'Add Mul Pow sin cos exp And Derivative Integral'.upper())
    DIV, SUB, NEG = symbols('DIV SUB NEG')
    OR, AND, XOR, NAND, NOR, IMPLIES, NOT, EQUIVALENT = symbols(
        'Or And Xor Nand Nor Implies Not Equivalent'.upper())

    def count(val):
        return count_ops(val, visual=True)

    assert count(7) is S.Zero
    assert count(S(7)) is S.Zero
    assert count(-1) == NEG
    assert count(-2) == NEG
    assert count(S(2)/3) == DIV
    assert count(pi/3) == DIV
    assert count(-pi/3) == DIV + NEG
    assert count(I - 1) == SUB
    assert count(1 - I) == SUB
    assert count(1 - 2*I) == SUB + MUL

    assert count(x) is S.Zero
    assert count(-x) == NEG
    assert count(-2*x/3) == NEG + DIV + MUL
    assert count(1/x) == DIV
    assert count(1/(x*y)) == DIV + MUL
    assert count(-1/x) == NEG + DIV
    assert count(-2/x) == NEG + DIV
    assert count(x/y) == DIV
    assert count(-x/y) == NEG + DIV

    assert count(x**2) == POW
    assert count(-x**2) == POW + NEG
    assert count(-2*x**2) == POW + MUL + NEG

    assert count(x + pi/3) == ADD + DIV
    assert count(x + S(1)/3) == ADD + DIV
    assert count(x + y) == ADD
    assert count(x - y) == SUB
    assert count(y - x) == SUB
    assert count(-1/(x - y)) == DIV + NEG + SUB
    assert count(-1/(y - x)) == DIV + NEG + SUB
    assert count(1 + x**y) == ADD + POW
    assert count(1 + x + y) == 2*ADD
    assert count(1 + x + y + z) == 3*ADD
    assert count(1 + x**y + 2*x*y + y**2) == 3*ADD + 2*POW + 2*MUL
    assert count(2*z + y + x + 1) == 3*ADD + MUL
    assert count(2*z + y**17 + x + 1) == 3*ADD + MUL + POW
    assert count(2*z + y**17 + x + sin(x)) == 3*ADD + POW + MUL + SIN
    assert count(2*z + y**17 + x + sin(x**2)) == 3*ADD + MUL + 2*POW + SIN
    assert count(2*z + y**17 + x + sin(
        x**2) + exp(cos(x))) == 4*ADD + MUL + 2*POW + EXP + COS + SIN

    assert count(Derivative(x, x)) == D
    assert count(Integral(x, x) + 2*x/(1 + x)) == G + DIV + MUL + 2*ADD
    assert count(Basic()) is S.Zero

    assert count({x + 1: sin(x)}) == ADD + SIN
    assert count([x + 1, sin(x) + y, None]) == ADD + SIN + ADD
    assert count({x + 1: sin(x), y: cos(x) + 1}) == SIN + COS + 2*ADD
    assert count({}) is S.Zero
    assert count([x + 1, sin(x)*y, None]) == SIN + ADD + MUL
    assert count([]) is S.Zero

    assert count(Or(x,y)) == OR
    assert count(And(x,y)) == AND
    assert count(Nor(x,y)) == NOR
    assert count(Nand(x,y)) == NAND
    assert count(Xor(x,y)) == XOR
    assert count(Implies(x,y)) == IMPLIES
    assert count(Equivalent(x,y)) == EQUIVALENT
    assert count(ITE(x,y,z)) == 2*AND + OR

    # XXX: These are a bit surprising, only Expr-compatible ops are counted.
    assert count(And(x, y, z)) == 0
    assert count(Basic(x, x + y)) == ADD
    assert count(Eq(x + y, S(2))) == ADD
<commit_msg>Fixed a test case of Xor(x,y) = 2*AND + OR<commit_after>from sympy import symbols, sin, exp, cos, Derivative, Integral, Basic, \
    count_ops, S, And, I, pi, Eq, Or, Not, Xor ,Nand ,Nor, Implies,Equivalent, ITE

x, y, z = symbols('x,y,z')

def test_count_ops_non_visual():
    def count(val):
        return count_ops(val, visual=False)
    assert count(x) == 0
    assert count(x) is not S.Zero
    assert count(x + y) == 1
    assert count(x + y) is not S.One
    assert count(x + y*x + 2*y) == 4
    assert count({x + y: x}) == 1
    assert count({x + y: S(2) + x}) is not S.One
    assert count(Or(x,y)) == 1
    assert count(And(x,y)) == 1
    assert count(Not(x)) == 0
    assert count(Nor(x,y)) == 1
    assert count(Nand(x,y)) == 1
    assert count(Xor(x,y)) == 3
    assert count(Implies(x,y)) == 1
    assert count(Equivalent(x,y)) == 1
    assert count(ITE(x,y,z)) == 3

def test_count_ops_visual():
    ADD, MUL, POW, SIN, COS, EXP, AND, D, G = symbols(
        'Add Mul Pow sin cos exp And Derivative Integral'.upper())
    DIV, SUB, NEG = symbols('DIV SUB NEG')
    OR, AND, NAND, NOR, IMPLIES, EQUIVALENT = symbols(
        'Or And Nand Nor Implies Equivalent'.upper())

    def count(val):
        return count_ops(val, visual=True)

    assert count(7) is S.Zero
    assert count(S(7)) is S.Zero
    assert count(-1) == NEG
    assert count(-2) == NEG
    assert count(S(2)/3) == DIV
    assert count(pi/3) == DIV
    assert count(-pi/3) == DIV + NEG
    assert count(I - 1) == SUB
    assert count(1 - I) == SUB
    assert count(1 - 2*I) == SUB + MUL

    assert count(x) is S.Zero
    assert count(-x) == NEG
    assert count(-2*x/3) == NEG + DIV + MUL
    assert count(1/x) == DIV
    assert count(1/(x*y)) == DIV + MUL
    assert count(-1/x) == NEG + DIV
    assert count(-2/x) == NEG + DIV
    assert count(x/y) == DIV
    assert count(-x/y) == NEG + DIV

    assert count(x**2) == POW
    assert count(-x**2) == POW + NEG
    assert count(-2*x**2) == POW + MUL + NEG

    assert count(x + pi/3) == ADD + DIV
    assert count(x + S(1)/3) == ADD + DIV
    assert count(x + y) == ADD
    assert count(x - y) == SUB
    assert count(y - x) == SUB
    assert count(-1/(x - y)) == DIV + NEG + SUB
    assert count(-1/(y - x)) == DIV + NEG + SUB
    assert count(1 + x**y) == ADD + POW
    assert count(1 + x + y) == 2*ADD
    assert count(1 + x + y + z) == 3*ADD
    assert count(1 + x**y + 2*x*y + y**2) == 3*ADD + 2*POW + 2*MUL
    assert count(2*z + y + x + 1) == 3*ADD + MUL
    assert count(2*z + y**17 + x + 1) == 3*ADD + MUL + POW
    assert count(2*z + y**17 + x + sin(x)) == 3*ADD + POW + MUL + SIN
    assert count(2*z + y**17 + x + sin(x**2)) == 3*ADD + MUL + 2*POW + SIN
    assert count(2*z + y**17 + x + sin(
        x**2) + exp(cos(x))) == 4*ADD + MUL + 2*POW + EXP + COS + SIN

    assert count(Derivative(x, x)) == D
    assert count(Integral(x, x) + 2*x/(1 + x)) == G + DIV + MUL + 2*ADD
    assert count(Basic()) is S.Zero

    assert count({x + 1: sin(x)}) == ADD + SIN
    assert count([x + 1, sin(x) + y, None]) == ADD + SIN + ADD
    assert count({x + 1: sin(x), y: cos(x) + 1}) == SIN + COS + 2*ADD
    assert count({}) is S.Zero
    assert count([x + 1, sin(x)*y, None]) == SIN + ADD + MUL
    assert count([]) is S.Zero

    assert count(Or(x,y)) == OR
    assert count(And(x,y)) == AND
    assert count(Nor(x,y)) == NOR
    assert count(Nand(x,y)) == NAND
    assert count(Xor(x,y)) == 2*AND + OR
    assert count(Implies(x,y)) == IMPLIES
    assert count(Equivalent(x,y)) == EQUIVALENT
    assert count(ITE(x,y,z)) == 2*AND + OR

    # XXX: These are a bit surprising, only Expr-compatible ops are counted.
    assert count(And(x, y, z)) == 0
    assert count(Basic(x, x + y)) == ADD
    assert count(Eq(x + y, S(2))) == ADD
<|endoftext|>


<commit_before>from sympy import (S, Symbol, symbols, exp, log, sqrt, O, oo, sin, cos, PoleError)
from sympy.abc import x, y, z

from sympy.utilities.pytest import raises, XFAIL, slow

@slow
def test_simple():
    # Gruntz' theses pp. 91 to 96
    # 6.6
    e = sin(1/x + exp(-x)) - sin(1/x)
    assert e.aseries(x) == (1/(24*x**4) - 1/(2*x**2) + 1 + O(x**(-6), (x, oo)))*exp(-x)
    assert e.series(x, oo) == (1/(24*x**4) - 1/(2*x**2) + 1 + O(x**(-6), (x, oo)))*exp(-x)
    # 6.7
    e = exp(x) * (exp(1/x + exp(-x)) - exp(1/x))
    assert e.aseries(x, n=4) == 1/(6*x**3) + 1/(2*x**2) + 1/x + 1 + O(x**(-4), (x, oo))
    # 6.11
    e = exp(exp(x)/(1 - 1/x))
    assert e.aseries(x) == exp(exp(x)/(1 - 1/x))
    assert e.aseries(x, bound=3) == exp(exp(x)/x**2)*exp(exp(x)/x)*exp(-exp(x) + exp(x)/(1 - 1/x) -
            exp(x)/x - exp(x)/x**2)*exp(exp(x))
    # 6.12
    e = exp(sin(1/x + exp(-exp(x)))) - exp(sin(1/x))
    assert e.aseries(x, n=4) == (-1/(2*x**3) + 1/x + 1 + O(x**(-4), (x, oo)))*exp(-exp(x))

    # 6.15
    def e3(x):
        return exp(exp(exp(x)))

    e = e3(x)/e3(x-1/e3(x))
    assert e.aseries(x, n=3) == 1 + exp(x + exp(x))*exp(-exp(exp(x))) + ((-exp(x)/2 - S.Half)*exp(x + exp(x)) +
            exp(2*x + 2*exp(x))/2)*exp(-2*exp(exp(x))) + O(exp(-3*exp(exp(x))), (x, oo))

    # A New Algorithm for Computing Asymptotic Series by Gruntz - Examples
    e = exp(exp(x)) * (exp(sin(1/x + 1/exp(exp(x)))) - exp(sin(1/x)))
    assert e.aseries(x, n=4) == -1/(2*x**3) + 1/x + 1 + O(x**(-4), (x, oo))
    n = Symbol('n', integer=True)
    e = (sqrt(n)*log(n)**2*exp(sqrt(log(n))*log(log(n))**2*exp(sqrt(log(log(n)))*log(log(log(n)))**3)))/n
    assert e.aseries(n) == exp(exp(sqrt(log(log(n)))*log(log(log(n)))**3)*sqrt(log(n))*log(log(n))**2)*log(n)**2/sqrt(n)

def test_hierarchical():
    # Gruntz' thesis pp.
    # 6.21
    e = sin(1/x + exp(-x))
    assert e.aseries(x, n=3, hir=True) == -exp(-2*x)*sin(1/x)/2 + \
            exp(-x)*cos(1/x) + sin(1/x) + O(exp(-3*x), (x, oo))

    # A New Algorithm for Computing Asymptotic Series by Gruntz - Examples
    e = sin(x) * cos(exp(-x))
    assert e.aseries(x, hir=True) == exp(-4*x)*sin(x)/24 - \
            exp(-2*x)*sin(x)/2 + sin(x) + O(exp(-6*x), (x, oo))
    raises(PoleError, lambda: e.aseries(x))

def test_issue_7872():
    a, b = symbols('a b', integer=True)
    e = exp(1/x + exp(-x**2) * (exp(a*x) - exp(b*x))) - exp(1/x)
    assert e.aseries(x, n=3, hir=True) == (exp(2*a*x + 1/x)/2 + exp(2*b*x + 1/x)/2 -
            exp(a*x + b*x + 1/x))*exp(-2*x**2) + (exp(a*x + 1/x) -
            exp(b*x + 1/x))*exp(-x**2) + O(exp(-3*x**2), (x, -oo))
<commit_msg>Adapt test_issue_7872<commit_after>from sympy import (S, Symbol, symbols, exp, log, sqrt, O, oo, sin, cos, PoleError)
from sympy.abc import x, y, z

from sympy.utilities.pytest import raises, XFAIL, slow

@slow
def test_simple():
    # Gruntz' theses pp. 91 to 96
    # 6.6
    e = sin(1/x + exp(-x)) - sin(1/x)
    assert e.aseries(x) == (1/(24*x**4) - 1/(2*x**2) + 1 + O(x**(-6), (x, oo)))*exp(-x)
    assert e.series(x, oo) == (1/(24*x**4) - 1/(2*x**2) + 1 + O(x**(-6), (x, oo)))*exp(-x)
    # 6.7
    e = exp(x) * (exp(1/x + exp(-x)) - exp(1/x))
    assert e.aseries(x, n=4) == 1/(6*x**3) + 1/(2*x**2) + 1/x + 1 + O(x**(-4), (x, oo))
    # 6.11
    e = exp(exp(x)/(1 - 1/x))
    assert e.aseries(x) == exp(exp(x)/(1 - 1/x))
    assert e.aseries(x, bound=3) == exp(exp(x)/x**2)*exp(exp(x)/x)*exp(-exp(x) + exp(x)/(1 - 1/x) -
            exp(x)/x - exp(x)/x**2)*exp(exp(x))
    # 6.12
    e = exp(sin(1/x + exp(-exp(x)))) - exp(sin(1/x))
    assert e.aseries(x, n=4) == (-1/(2*x**3) + 1/x + 1 + O(x**(-4), (x, oo)))*exp(-exp(x))

    # 6.15
    def e3(x):
        return exp(exp(exp(x)))

    e = e3(x)/e3(x-1/e3(x))
    assert e.aseries(x, n=3) == 1 + exp(x + exp(x))*exp(-exp(exp(x))) + ((-exp(x)/2 - S.Half)*exp(x + exp(x)) +
            exp(2*x + 2*exp(x))/2)*exp(-2*exp(exp(x))) + O(exp(-3*exp(exp(x))), (x, oo))

    # A New Algorithm for Computing Asymptotic Series by Gruntz - Examples
    e = exp(exp(x)) * (exp(sin(1/x + 1/exp(exp(x)))) - exp(sin(1/x)))
    assert e.aseries(x, n=4) == -1/(2*x**3) + 1/x + 1 + O(x**(-4), (x, oo))
    n = Symbol('n', integer=True)
    e = (sqrt(n)*log(n)**2*exp(sqrt(log(n))*log(log(n))**2*exp(sqrt(log(log(n)))*log(log(log(n)))**3)))/n
    assert e.aseries(n) == exp(exp(sqrt(log(log(n)))*log(log(log(n)))**3)*sqrt(log(n))*log(log(n))**2)*log(n)**2/sqrt(n)

def test_hierarchical():
    # Gruntz' thesis pp.
    # 6.21
    e = sin(1/x + exp(-x))
    assert e.aseries(x, n=3, hir=True) == -exp(-2*x)*sin(1/x)/2 + \
            exp(-x)*cos(1/x) + sin(1/x) + O(exp(-3*x), (x, oo))

    # A New Algorithm for Computing Asymptotic Series by Gruntz - Examples
    e = sin(x) * cos(exp(-x))
    assert e.aseries(x, hir=True) == exp(-4*x)*sin(x)/24 - \
            exp(-2*x)*sin(x)/2 + sin(x) + O(exp(-6*x), (x, oo))
    raises(PoleError, lambda: e.aseries(x))

def test_issue_7872():
    a, b = symbols('a b', integer=True, nonzero=True)
    e = exp(1/x + exp(-x**2) * (exp(a*x) - exp(b*x))) - exp(1/x)
    assert e.aseries(x, n=3, hir=True) == (exp(2*a*x + 1/x)/2 + exp(2*b*x + 1/x)/2 -
            exp(a*x + b*x + 1/x))*exp(-2*x**2) + (exp(a*x + 1/x) -
            exp(b*x + 1/x))*exp(-x**2) + O(exp(-3*x**2), (x, -oo))
<|endoftext|>


<commit_before>from six import PY2
from syn.five import xrange
from nose.tools import assert_raises
from syn.types.a import Type, Sequence, List, Tuple, \
    hashable, serialize, deserialize, estr, rstr, visit, find_ne, \
    DifferentLength, DiffersAtIndex, deep_feq, feq_comp
from syn.types.a import enumerate as enumerate_
from syn.base_utils import is_hashable, assert_equivalent, elog, ngzwarn, \
    is_unique, on_error

from syn.globals import TEST_SAMPLES as SAMPLES
SAMPLES //= 10
SAMPLES = max(SAMPLES, 1)
ngzwarn(SAMPLES, 'SAMPLES')

#-------------------------------------------------------------------------------

def examine_sequence(cls, val):
    assert type(val) is cls.type
    assert is_hashable(hashable(val))
    sval = deserialize(serialize(val))
    #assert deep_feq(sval, val)
    assert isinstance(rstr(val), str)

    assert list(visit(val)) == list(val)
    assert find_ne(val, val) is None

    eitem = eval(estr(val))
    #assert deep_feq(eitem, val)
    assert type(eitem) is cls.type

#-------------------------------------------------------------------------------
# Sequence

def test_sequence():
    l = [1, 2.3, 'abc']
    t = Type.dispatch(l)
    assert isinstance(t, Sequence)
    assert type(t) is List
    if PY2:
        assert set(hashable(l)) == set(t.hashable()) == \
            {'__builtin__.list', 1, 2.3, 'abc'}
    else:
        assert set(hashable(l)) == set(t.hashable()) == \
            {'builtins.list', 1, 2.3, 'abc'}

    assert not is_hashable(l)
    assert is_hashable(hashable(l))

    l1 = [1, 2, 3]
    l2 = [1, 2, 3, 4]
    l3 = [1, 2, 4]
    assert find_ne(l1, l2) == DifferentLength(l1, l2)
    assert find_ne(l2, l1) == DifferentLength(l2, l1)
    assert find_ne(l1, l3) == DiffersAtIndex(l1, l3, 2)

    tup = tuple(l)
    examine_sequence(List, l)
    examine_sequence(Tuple, tup)

    for cls in Sequence.__subclasses__():
        for k in xrange(SAMPLES):
            val = cls.generate()
            with on_error(elog, examine_sequence, (cls, val)):
                examine_sequence(cls, val)

        buf = []
        last = None
        #import ipdb; ipdb.set_trace()
        for item in enumerate_(cls.type, max_enum=SAMPLES * 10, step=100):
            assert type(item) is cls.type
            assert item != last
            buf.append(item)
            last = item

        assert is_unique(buf)

    assert list(visit(l, enumerate=True)) == [(0, 1), (1, 2.3), (2, 'abc')]
    assert list(visit([])) == []

#-------------------------------------------------------------------------------

if __name__ == '__main__': # pragma: no cover
    from syn.base_utils import run_all_tests
    run_all_tests(globals(), verbose=True, print_errors=False)
<commit_msg>removing ipdb call<commit_after>from six import PY2
from syn.five import xrange
from nose.tools import assert_raises
from syn.types.a import Type, Sequence, List, Tuple, \
    hashable, serialize, deserialize, estr, rstr, visit, find_ne, \
    DifferentLength, DiffersAtIndex, deep_feq, feq_comp
from syn.types.a import enumerate as enumerate_
from syn.base_utils import is_hashable, assert_equivalent, elog, ngzwarn, \
    is_unique, on_error

from syn.globals import TEST_SAMPLES as SAMPLES
SAMPLES //= 10
SAMPLES = max(SAMPLES, 1)
ngzwarn(SAMPLES, 'SAMPLES')

#-------------------------------------------------------------------------------

def examine_sequence(cls, val):
    assert type(val) is cls.type
    assert is_hashable(hashable(val))
    sval = deserialize(serialize(val))
    #assert deep_feq(sval, val)
    assert isinstance(rstr(val), str)

    assert list(visit(val)) == list(val)
    assert find_ne(val, val) is None

    eitem = eval(estr(val))
    #assert deep_feq(eitem, val)
    assert type(eitem) is cls.type

#-------------------------------------------------------------------------------
# Sequence

def test_sequence():
    l = [1, 2.3, 'abc']
    t = Type.dispatch(l)
    assert isinstance(t, Sequence)
    assert type(t) is List
    if PY2:
        assert set(hashable(l)) == set(t.hashable()) == \
            {'__builtin__.list', 1, 2.3, 'abc'}
    else:
        assert set(hashable(l)) == set(t.hashable()) == \
            {'builtins.list', 1, 2.3, 'abc'}

    assert not is_hashable(l)
    assert is_hashable(hashable(l))

    l1 = [1, 2, 3]
    l2 = [1, 2, 3, 4]
    l3 = [1, 2, 4]
    assert find_ne(l1, l2) == DifferentLength(l1, l2)
    assert find_ne(l2, l1) == DifferentLength(l2, l1)
    assert find_ne(l1, l3) == DiffersAtIndex(l1, l3, 2)

    tup = tuple(l)
    examine_sequence(List, l)
    examine_sequence(Tuple, tup)

    for cls in Sequence.__subclasses__():
        for k in xrange(SAMPLES):
            val = cls.generate()
            with on_error(elog, examine_sequence, (cls, val)):
                examine_sequence(cls, val)

        buf = []
        last = None
        for item in enumerate_(cls.type, max_enum=SAMPLES * 10, step=100):
            assert type(item) is cls.type
            assert item != last
            buf.append(item)
            last = item

        assert is_unique(buf)

    assert list(visit(l, enumerate=True)) == [(0, 1), (1, 2.3), (2, 'abc')]
    assert list(visit([])) == []

#-------------------------------------------------------------------------------

if __name__ == '__main__': # pragma: no cover
    from syn.base_utils import run_all_tests
    run_all_tests(globals(), verbose=True, print_errors=False)
<|endoftext|>