<commit_before># BenchExec is a framework for reliable benchmarking.
# This file is part of BenchExec.
#
# Copyright (C) 2007-2016  Dirk Beyer
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import collections
import contextlib
import errno
import functools
import inspect
import logging
import multiprocessing
import os
import signal
import tempfile

from benchexec import BenchExecException, container, containerexecutor, libc, util
import benchexec.tools.template

class ContainerizedTool(benchexec.tools.template.BaseTool):
    """Wrapper for an instance of any subclass of benchexec.tools.template.BaseTool.
    The module and the subclass instance will be loaded in a subprocess that has been
    put into a container. This means, for example, that the code of this module cannot
    make network connections and that any changes made to files on disk have no effect.
    """

    def __init__(self, tool_module, config):
        """Load tool-info module in subprocess.
        @param tool_module: The name of the module to load.
            Needs to define class named Tool.
        @param config: A config object suitable for
            benchexec.containerexecutor.handle_basic_container_args()
        """
        # We use multiprocessing.Pool as an easy way for RPC with another process.
        self._pool = multiprocessing.Pool(1, _init_worker_process)

        container_options = containerexecutor.handle_basic_container_args(config)
        temp_dir = tempfile.mkdtemp(prefix="Benchexec_tool_info_container_")

        # Call function that loads tool module and returns its doc
        try:
            self.__doc__ = self._pool.apply(
                _init_container_and_load_tool,
                [tool_module, temp_dir],
                container_options,
            )
        finally:
            # Outside the container, the temp_dir is just an empty directory, because
            # the tmpfs mount is only visible inside. We can remove it immediately.
            with contextlib.suppress(OSError):
                os.rmdir(temp_dir)

    def _forward_call(self, method_name, args, kwargs):
        """Call given method indirectly on the tool instance in the container."""
        return self._pool.apply(_call_tool_func, [method_name, list(args), kwargs])

    @classmethod
    def _add_proxy_function(cls, method_name, method):
        """Add function to given class that calls the specified method indirectly."""

        @functools.wraps(method)  # lets proxy_function look like method (name and doc)
        def proxy_function(self, *args, **kwargs):
            return self._forward_call(method_name, args, kwargs)

        setattr(cls, member_name, proxy_function)

    # All methods inherited from BaseTool will be overwritten by the following loop

for member_name, member in inspect.getmembers(ContainerizedTool, inspect.isfunction):
    if member_name[0] == "_":
        continue
    ContainerizedTool._add_proxy_function(member_name, member)

def _init_worker_process():
    """Initial setup of worker process from multiprocessing module."""

    # Need to reset signal handling because multiprocessing relies on SIGTERM
    # but benchexec adds a handler for it.
    signal.signal(signal.SIGTERM, signal.SIG_DFL)

    # If Ctrl+C is pressed, each process receives SIGINT. We need to ignore it because
    # concurrent worker threads of benchexec might still attempt to use the tool-info
    # module until all of them are stopped, so this process must stay alive.
    signal.signal(signal.SIGINT, signal.SIG_IGN)

def _init_container_and_load_tool(tool_module, *args, **kwargs):
    """Initialize container for the current process and load given tool-info module."""
    try:
        _init_container(*args, **kwargs)
    except EnvironmentError as e:
        raise BenchExecException("Failed to configure container: " + str(e))
    return _load_tool(tool_module)

def _init_container(
    temp_dir,
    network_access,
    dir_modes,
    container_system_config,
    container_tmpfs,  # ignored, tmpfs is always used
):
    """
    Create a fork of this process in a container. This method only returns in the fork,
    so calling it seems like moving the current process into a container.
    """
    # Prepare for private home directory, some tools write there
    if container_system_config:
        dir_modes.setdefault(container.CONTAINER_HOME, container.DIR_HIDDEN)
        os.environ["HOME"] = container.CONTAINER_HOME

    # Preparations
    temp_dir = temp_dir.encode()
    dir_modes = collections.OrderedDict(
        sorted(
            ((path.encode(), kind) for (path, kind) in dir_modes.items()),
            key=lambda tupl: len(tupl[0]),
        )
    )
    uid = container.CONTAINER_UID if container_system_config else os.getuid()
    gid = container.CONTAINER_GID if container_system_config else os.getgid()

    # Create container.
    # Contrary to ContainerExecutor, which uses clone to start a new process in new
    # namespaces, we use unshare, which puts the current process (the multiprocessing
    # worker process) into new namespaces.
    # The exception is the PID namespace, which will only apply to children processes.
    flags = (
        libc.CLONE_NEWNS
        | libc.CLONE_NEWUTS
        | libc.CLONE_NEWIPC
        | libc.CLONE_NEWUSER
        | libc.CLONE_NEWPID
    )
    if not network_access:
        flags |= libc.CLONE_NEWNET
    try:
        libc.unshare(flags)
    except OSError as e:
        if (
            e.errno == errno.EPERM
            and util.try_read_file("/proc/sys/kernel/unprivileged_userns_clone") == "0"
        ):
            raise BenchExecException(
                "Unprivileged user namespaces forbidden on this system, please "
                "enable them with 'sysctl kernel.unprivileged_userns_clone=1' "
                "or disable container mode"
            )
        else:
            raise BenchExecException(
                "Creating namespace for container mode failed: " + os.strerror(e.errno)
            )

    # Container config
    container.setup_user_mapping(os.getpid(), uid, gid)
    _setup_container_filesystem(temp_dir, dir_modes, container_system_config)
    if container_system_config:
        libc.sethostname(container.CONTAINER_HOSTNAME)
    if not network_access:
        container.activate_network_interface("lo")

    # Because this process is not actually in the new PID namespace, we fork.
    # The child will be in the new PID namespace and will assume the role of the acting
    # multiprocessing worker (which it can do because it inherits the file descriptors
    # that multiprocessing uses for communication).
    # The original multiprocessing worker (the parent of the fork) must do nothing in
    # order to not confuse multiprocessing.
    pid = os.fork()
    if pid:
        container.drop_capabilities()
        # block parent such that it does nothing
        os.waitpid(pid, 0)
        os._exit(0)

    # Finalize container setup in child
    container.mount_proc(container_system_config)  # only possible in child
    container.drop_capabilities()
    libc.prctl(libc.PR_SET_DUMPABLE, libc.SUID_DUMP_DISABLE, 0, 0, 0)
    container.setup_seccomp_filter()

def _load_tool(tool_module):
    logging.debug("Loading tool-info module %s in container", tool_module)
    global tool
    tool = __import__(tool_module, fromlist=["Tool"]).Tool()
    return tool.__doc__

def _setup_container_filesystem(temp_dir, dir_modes, container_system_config):
    # We put all temp files on a RAM disk
    libc.mount(None, temp_dir, b"tmpfs", 0, b"size=100%")

    mount_base = os.path.join(temp_dir, b"mount")  # base dir for container mounts
    temp_base = os.path.join(temp_dir, b"temp")  # upper layer for overlayfs
    work_base = os.path.join(temp_dir, b"overlayfs")  # work dir for overlayfs
    os.mkdir(mount_base)
    os.mkdir(temp_base)
    os.mkdir(work_base)
    container.duplicate_mount_hierarchy(mount_base, temp_base, work_base, dir_modes)

    def make_tmpfs_dir(path):
        """Ensure that a tmpfs is mounted on path, if the path exists"""
        if path in dir_modes:
            return  # explicitly configured by user
        mount_tmpfs = mount_base + path
        if os.path.isdir(mount_tmpfs):
            temp_tmpfs = temp_base + path
            util.makedirs(temp_tmpfs, exist_ok=True)
            container.make_bind_mount(temp_tmpfs, mount_tmpfs)

    make_tmpfs_dir(b"/dev/shm")
    make_tmpfs_dir(b"/run/shm")

    if container_system_config:
        container.setup_container_system_config(temp_base, mount_base, dir_modes)

    cwd = os.getcwd()
    container.chroot(mount_base)
    os.chdir(cwd)

def _call_tool_func(name, args, kwargs):
    """Call a method on the tool instance.
    @param name: The method name to call.
    @param args: List of arguments to be passed as positional arguments.
    @param kwargs: Dict of arguments to be passed as keyword arguments.
    """
    global tool
    return getattr(tool, name)(*args, **kwargs)
<commit_msg>Fix #449: benchexec hangs in container mode if tool cannot be executed<commit_after># BenchExec is a framework for reliable benchmarking.
# This file is part of BenchExec.
#
# Copyright (C) 2007-2016  Dirk Beyer
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import collections
import contextlib
import errno
import functools
import inspect
import logging
import multiprocessing
import os
import signal
import tempfile

from benchexec import BenchExecException, container, containerexecutor, libc, util
import benchexec.tools.template

class ContainerizedTool(benchexec.tools.template.BaseTool):
    """Wrapper for an instance of any subclass of benchexec.tools.template.BaseTool.
    The module and the subclass instance will be loaded in a subprocess that has been
    put into a container. This means, for example, that the code of this module cannot
    make network connections and that any changes made to files on disk have no effect.
    """

    def __init__(self, tool_module, config):
        """Load tool-info module in subprocess.
        @param tool_module: The name of the module to load.
            Needs to define class named Tool.
        @param config: A config object suitable for
            benchexec.containerexecutor.handle_basic_container_args()
        """
        # We use multiprocessing.Pool as an easy way for RPC with another process.
        self._pool = multiprocessing.Pool(1, _init_worker_process)

        container_options = containerexecutor.handle_basic_container_args(config)
        temp_dir = tempfile.mkdtemp(prefix="Benchexec_tool_info_container_")

        # Call function that loads tool module and returns its doc
        try:
            self.__doc__ = self._pool.apply(
                _init_container_and_load_tool,
                [tool_module, temp_dir],
                container_options,
            )
        finally:
            # Outside the container, the temp_dir is just an empty directory, because
            # the tmpfs mount is only visible inside. We can remove it immediately.
            with contextlib.suppress(OSError):
                os.rmdir(temp_dir)

    def _forward_call(self, method_name, args, kwargs):
        """Call given method indirectly on the tool instance in the container."""
        return self._pool.apply(_call_tool_func, [method_name, list(args), kwargs])

    @classmethod
    def _add_proxy_function(cls, method_name, method):
        """Add function to given class that calls the specified method indirectly."""

        @functools.wraps(method)  # lets proxy_function look like method (name and doc)
        def proxy_function(self, *args, **kwargs):
            return self._forward_call(method_name, args, kwargs)

        setattr(cls, member_name, proxy_function)

    # All methods inherited from BaseTool will be overwritten by the following loop

for member_name, member in inspect.getmembers(ContainerizedTool, inspect.isfunction):
    if member_name[0] == "_":
        continue
    ContainerizedTool._add_proxy_function(member_name, member)

def _init_worker_process():
    """Initial setup of worker process from multiprocessing module."""

    # Need to reset signal handling because multiprocessing relies on SIGTERM
    # but benchexec adds a handler for it.
    signal.signal(signal.SIGTERM, signal.SIG_DFL)

    # If Ctrl+C is pressed, each process receives SIGINT. We need to ignore it because
    # concurrent worker threads of benchexec might still attempt to use the tool-info
    # module until all of them are stopped, so this process must stay alive.
    signal.signal(signal.SIGINT, signal.SIG_IGN)

def _init_container_and_load_tool(tool_module, *args, **kwargs):
    """Initialize container for the current process and load given tool-info module."""
    try:
        _init_container(*args, **kwargs)
    except EnvironmentError as e:
        raise BenchExecException("Failed to configure container: " + str(e))
    return _load_tool(tool_module)

def _init_container(
    temp_dir,
    network_access,
    dir_modes,
    container_system_config,
    container_tmpfs,  # ignored, tmpfs is always used
):
    """
    Create a fork of this process in a container. This method only returns in the fork,
    so calling it seems like moving the current process into a container.
    """
    # Prepare for private home directory, some tools write there
    if container_system_config:
        dir_modes.setdefault(container.CONTAINER_HOME, container.DIR_HIDDEN)
        os.environ["HOME"] = container.CONTAINER_HOME

    # Preparations
    temp_dir = temp_dir.encode()
    dir_modes = collections.OrderedDict(
        sorted(
            ((path.encode(), kind) for (path, kind) in dir_modes.items()),
            key=lambda tupl: len(tupl[0]),
        )
    )
    uid = container.CONTAINER_UID if container_system_config else os.getuid()
    gid = container.CONTAINER_GID if container_system_config else os.getgid()

    # Create container.
    # Contrary to ContainerExecutor, which uses clone to start a new process in new
    # namespaces, we use unshare, which puts the current process (the multiprocessing
    # worker process) into new namespaces.
    # The exception is the PID namespace, which will only apply to children processes.
    flags = (
        libc.CLONE_NEWNS
        | libc.CLONE_NEWUTS
        | libc.CLONE_NEWIPC
        | libc.CLONE_NEWUSER
        | libc.CLONE_NEWPID
    )
    if not network_access:
        flags |= libc.CLONE_NEWNET
    try:
        libc.unshare(flags)
    except OSError as e:
        if (
            e.errno == errno.EPERM
            and util.try_read_file("/proc/sys/kernel/unprivileged_userns_clone") == "0"
        ):
            raise BenchExecException(
                "Unprivileged user namespaces forbidden on this system, please "
                "enable them with 'sysctl kernel.unprivileged_userns_clone=1' "
                "or disable container mode"
            )
        else:
            raise BenchExecException(
                "Creating namespace for container mode failed: " + os.strerror(e.errno)
            )

    # Container config
    container.setup_user_mapping(os.getpid(), uid, gid)
    _setup_container_filesystem(temp_dir, dir_modes, container_system_config)
    if container_system_config:
        libc.sethostname(container.CONTAINER_HOSTNAME)
    if not network_access:
        container.activate_network_interface("lo")

    # Because this process is not actually in the new PID namespace, we fork.
    # The child will be in the new PID namespace and will assume the role of the acting
    # multiprocessing worker (which it can do because it inherits the file descriptors
    # that multiprocessing uses for communication).
    # The original multiprocessing worker (the parent of the fork) must do nothing in
    # order to not confuse multiprocessing.
    pid = os.fork()
    if pid:
        container.drop_capabilities()
        # block parent such that it does nothing
        os.waitpid(pid, 0)
        os._exit(0)

    # Finalize container setup in child
    container.mount_proc(container_system_config)  # only possible in child
    container.drop_capabilities()
    libc.prctl(libc.PR_SET_DUMPABLE, libc.SUID_DUMP_DISABLE, 0, 0, 0)
    container.setup_seccomp_filter()

def _load_tool(tool_module):
    logging.debug("Loading tool-info module %s in container", tool_module)
    global tool
    tool = __import__(tool_module, fromlist=["Tool"]).Tool()
    return tool.__doc__

def _setup_container_filesystem(temp_dir, dir_modes, container_system_config):
    # We put all temp files on a RAM disk
    libc.mount(None, temp_dir, b"tmpfs", 0, b"size=100%")

    mount_base = os.path.join(temp_dir, b"mount")  # base dir for container mounts
    temp_base = os.path.join(temp_dir, b"temp")  # upper layer for overlayfs
    work_base = os.path.join(temp_dir, b"overlayfs")  # work dir for overlayfs
    os.mkdir(mount_base)
    os.mkdir(temp_base)
    os.mkdir(work_base)
    container.duplicate_mount_hierarchy(mount_base, temp_base, work_base, dir_modes)

    def make_tmpfs_dir(path):
        """Ensure that a tmpfs is mounted on path, if the path exists"""
        if path in dir_modes:
            return  # explicitly configured by user
        mount_tmpfs = mount_base + path
        if os.path.isdir(mount_tmpfs):
            temp_tmpfs = temp_base + path
            util.makedirs(temp_tmpfs, exist_ok=True)
            container.make_bind_mount(temp_tmpfs, mount_tmpfs)

    make_tmpfs_dir(b"/dev/shm")
    make_tmpfs_dir(b"/run/shm")

    if container_system_config:
        container.setup_container_system_config(temp_base, mount_base, dir_modes)

    cwd = os.getcwd()
    container.chroot(mount_base)
    os.chdir(cwd)

def _call_tool_func(name, args, kwargs):
    """Call a method on the tool instance.
    @param name: The method name to call.
    @param args: List of arguments to be passed as positional arguments.
    @param kwargs: Dict of arguments to be passed as keyword arguments.
    """
    global tool
    try:
        return getattr(tool, name)(*args, **kwargs)
    except SystemExit as e:
        # SystemExit would terminate the worker process instead of being propagated.
        raise BenchExecException(str(e.code))
<|endoftext|>


<commit_before>"""
Django settings for billstack project.

For more information on this file, see
https://docs.djangoproject.com/en/dev/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/dev/ref/settings/
"""

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'eohid!pxxl&cgv%-h_p70@@4)j_b(^l_782u1amcap-ryvc#t6'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

TEMPLATE_DEBUG = True

ALLOWED_HOSTS = []

# Application definition

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

ROOT_URLCONF = 'billstack.urls'

WSGI_APPLICATION = 'billstack.wsgi.application'

# Database
# https://docs.djangoproject.com/en/dev/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

# Internationalization
# https://docs.djangoproject.com/en/dev/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/dev/howto/static-files/

STATIC_URL = '/static/'
<commit_msg>Add local_settings from settings.py<commit_after>"""
Django settings for billstack project.

For more information on this file, see
https://docs.djangoproject.com/en/dev/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/dev/ref/settings/
"""

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'eohid!pxxl&cgv%-h_p70@@4)j_b(^l_782u1amcap-ryvc#t6'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

TEMPLATE_DEBUG = True

ALLOWED_HOSTS = []

# Application definition

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

ROOT_URLCONF = 'billstack.urls'

WSGI_APPLICATION = 'billstack.wsgi.application'

# Database
# https://docs.djangoproject.com/en/dev/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

# Internationalization
# https://docs.djangoproject.com/en/dev/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/dev/howto/static-files/

STATIC_URL = '/static/'

##############################LOCAL SETTINGS###########################

# local_settings.py can be used to override environment-specific settings
# like database and email that differ between development and production.

try:
    from local_settings import *
except ImportError:
    pass
<|endoftext|>


<commit_before>from nltk.corpus import brown
from nltk.corpus import gutenberg
from nltk.probability import FreqDist
from re import match
import os

class WordLoader(object):
    def __init__(self):
        self._max_words = 70000
        self._valid_words = set()

    def is_valid(self, word):
        return match(r'^[a-z]+$', word) is not None

    def load_valid_words(self, filename):
        f = open(filename)
        for word in f:
            stripped = word.strip()
            if(self.is_valid(stripped)):
                self._valid_words.add(stripped)
        f.close()

    def load_sorted_words(self, filename):
        with open(filename, 'rU') as f:
            self.sorted_words = f.readlines()
        self.sorted_words = map(str.rstrip, self.sorted_words)

    def write_sorted_words(self, all_words, filename):
        freqdist = self.find_frequent_words(all_words)
        self.sorted_words = freqdist.keys()
        with open(filename, 'w') as f:
            for word in self.sorted_words:
                f.write(word + '\n')

    def dump_words(self, all_words):
        """ Take a long list of words from several texts, and use them as a
        source for word frequencies. Then dump all the valid words, most
        frequent first. """

        is_summary_printed = False

        if is_summary_printed:
            print '%d words' % len(self._valid_words)
            print '-' * 10
        freq_list = self.find_frequent_words(all_words).keys()
        word_count = 0
        for word in freq_list:
            if word_count >= self._max_words:
                break
            print word
            word_count += 1
            self._valid_words.remove(word)
        for word in self._valid_words:
            if word_count >= self._max_words:
                break
            print word
            word_count += 1

        if is_summary_printed:
            print '-' * 10
            print '%d words' % word_count

    def merge_words(self, outer, inner, freqdist):
        pair_score = 0
        for i in range(1, len(outer)):
            prefix = outer[:i]
            suffix = outer[i:]
            combo = prefix + inner + suffix
            combo_freq = freqdist.freq(combo)
            if combo_freq > 0:
                if pair_score == 0:
                    pair_score = freqdist.freq(outer) * freqdist.freq(inner)
                words = [inner, outer]
                words.sort()
                score = combo_freq * pair_score
                min_length = min(len(inner), len(outer))
                if prefix in ('un', 'dis') and suffix == 'able':
                    continue
                if suffix in ('ing', 's', 'y'):
                    continue
                if suffix in ('d', 'ed') and combo.endswith('ed'):
                    continue
                if min_length >= 3:
                    print score, words[0], words[1]

    def print_sandwiches(self, all_words):
        freqdist = self.find_frequent_words(all_words)
        unique_words = freqdist.keys()
        word_count = len(unique_words)
        for i in range(word_count):
            word1 = unique_words[i]
            for j in range(i):
                word2 = unique_words[j]
                self.merge_words(word1, word2, freqdist)
                self.merge_words(word2, word1, freqdist)

    def find_frequent_words(self, all_words):
        freqdist = FreqDist(word.lower() for
            word in all_words if
            word.lower() in self._valid_words)
        return freqdist

    def filter_sandwiches(self, lines, all_words):
        freqdist = self.find_frequent_words(all_words)
        for line in lines:
            _, word1, word2 = line.split()
            self.merge_words(word1, word2, freqdist)
            self.merge_words(word2, word1, freqdist)

    def find_barconyms(self,
                       word,
                       reversed_word,
                       seen_words,
                       margin):
        for j in range(margin,len(word)-1-margin):
            for k in range(j+1, len(word)-margin):
                swapped_list = list(reversed_word)
                swapped_list[j], swapped_list[k] = (swapped_list[k],
                                                    swapped_list[j])
                swapped_word = ''.join(swapped_list)
                if swapped_word in seen_words:
                    if swapped_word == 'name':
                        print '---' + word
                    return swapped_word

    def find_bacronyms(self, sorted_words):
        seen_words = set()
        self.bacronyms = []
        barconyms = []
        bacromyns = []
        print 'Starting...'
        for i, word in enumerate(sorted_words):
#             if i > 2000:
#                 break
            if len(word) < 4:
                continue
            reversed_word = word[::-1]
            if reversed_word in seen_words:
                self.bacronyms.append(reversed_word)
            else:
                barconym = self.find_barconyms(word,
                                               reversed_word,
                                               seen_words,
                                               1)
                if barconym is not None:
                    barconyms.append(barconym)
                else:
                    bacromyn = self.find_barconyms(word,
                                                   reversed_word,
                                                   seen_words,
                                                   0)
                    if bacromyn is not None:
                        bacromyns.append(bacromyn)
                seen_words.add(word)

        self.barconyms = []
        self.bacromyns = []
        for word in barconyms:
            if (word not in self.bacronyms and
                word not in self.barconyms):

                self.barconyms.append(word)
        for word in bacromyns:
            if (word not in self.bacronyms and
                word not in self.barconyms and
                word not in self.bacromyns):

                self.bacromyns.append(word)
        print len(self.bacronyms), len(self.barconyms), len(self.bacromyns)
        bacromyns = self.barconyms[:]
        bacromyns.extend(self.bacromyns)
        output_count = min(len(self.bacronyms), len(bacromyns)/2)
        for i in range(output_count):
            words = {self.bacronyms[i],
                     bacromyns[2*i],
                     bacromyns[2*i+1]}
            print ' '.join(words)

if __name__ == '__main__':
    loader = WordLoader()
    sorted_words_filename = 'sorted_words.txt'
    if os.path.isfile(sorted_words_filename):
        loader.load_sorted_words(sorted_words_filename)
    else:
        loader.load_valid_words('/usr/share/dict/british-english')
        loader.load_valid_words('/usr/share/dict/american-english')
        all_words = brown.words() + gutenberg.words()
        loader.write_sorted_words(all_words, sorted_words_filename)

    loader.find_bacronyms(loader.sorted_words)
#     loader.dump_words(all_words)
#    loader.print_sandwiches(all_words)
#     f = open("/home/don/Documents/RussianDolls2.txt")
#     try:
#         loader.filter_sandwiches(f, all_words)
#     finally:
#         f.close()
elif __name__ == '__live_coding__':
    loader = WordLoader()
    is_valid = loader.is_valid('potato')
    loader.load_valid_words('sample.txt')
    loader.load_valid_words('sample2.txt')
    loader._valid_words = set("net ten dear read lengthy pear reap mean name amen".split())

    all_words = "net ten ten dear dear read read raed lengthy pear reap mean name name amen".split()
    loader.write_sorted_words(all_words, 'sample3.txt')

#     loader.dump_words(all_words)
#     loader.print_sandwiches(all_words)
    sorted_words = 'deer reed dear read lengthy name mean'.split()
    lines = ['1e-5 plain coming\n',
             '1e-6 ot a\n']
    loader.find_bacronyms(sorted_words)
<commit_msg>Use a score to sort barconyms in issue #35.<commit_after>from nltk.corpus import brown
from nltk.corpus import gutenberg
from nltk.probability import FreqDist
from re import match
import os

class WordLoader(object):
    def __init__(self):
        self._max_words = 70000
        self._valid_words = set()

    def is_valid(self, word):
        return match(r'^[a-z]+$', word) is not None

    def load_valid_words(self, filename):
        f = open(filename)
        for word in f:
            stripped = word.strip()
            if(self.is_valid(stripped)):
                self._valid_words.add(stripped)
        f.close()

    def load_sorted_words(self, filename):
        with open(filename, 'rU') as f:
            self.sorted_words = f.readlines()
        self.sorted_words = map(str.rstrip, self.sorted_words)

    def write_sorted_words(self, all_words, filename):
        freqdist = self.find_frequent_words(all_words)
        self.sorted_words = freqdist.keys()
        with open(filename, 'w') as f:
            for word in self.sorted_words:
                f.write(word + '\n')

    def dump_words(self, all_words):
        """ Take a long list of words from several texts, and use them as a
        source for word frequencies. Then dump all the valid words, most
        frequent first. """

        is_summary_printed = False

        if is_summary_printed:
            print '%d words' % len(self._valid_words)
            print '-' * 10
        freq_list = self.find_frequent_words(all_words).keys()
        word_count = 0
        for word in freq_list:
            if word_count >= self._max_words:
                break
            print word
            word_count += 1
            self._valid_words.remove(word)
        for word in self._valid_words:
            if word_count >= self._max_words:
                break
            print word
            word_count += 1

        if is_summary_printed:
            print '-' * 10
            print '%d words' % word_count

    def merge_words(self, outer, inner, freqdist):
        pair_score = 0
        for i in range(1, len(outer)):
            prefix = outer[:i]
            suffix = outer[i:]
            combo = prefix + inner + suffix
            combo_freq = freqdist.freq(combo)
            if combo_freq > 0:
                if pair_score == 0:
                    pair_score = freqdist.freq(outer) * freqdist.freq(inner)
                words = [inner, outer]
                words.sort()
                score = combo_freq * pair_score
                min_length = min(len(inner), len(outer))
                if prefix in ('un', 'dis') and suffix == 'able':
                    continue
                if suffix in ('ing', 's', 'y'):
                    continue
                if suffix in ('d', 'ed') and combo.endswith('ed'):
                    continue
                if min_length >= 3:
                    print score, words[0], words[1]

    def print_sandwiches(self, all_words):
        freqdist = self.find_frequent_words(all_words)
        unique_words = freqdist.keys()
        word_count = len(unique_words)
        for i in range(word_count):
            word1 = unique_words[i]
            for j in range(i):
                word2 = unique_words[j]
                self.merge_words(word1, word2, freqdist)
                self.merge_words(word2, word1, freqdist)

    def find_frequent_words(self, all_words):
        freqdist = FreqDist(word.lower() for
            word in all_words if
            word.lower() in self._valid_words)
        return freqdist

    def filter_sandwiches(self, lines, all_words):
        freqdist = self.find_frequent_words(all_words)
        for line in lines:
            _, word1, word2 = line.split()
            self.merge_words(word1, word2, freqdist)
            self.merge_words(word2, word1, freqdist)

    def find_barconyms(self,
                       word,
                       reversed_word,
                       frequency_score,
                       seen_words,
                       margin):
        for j in range(margin,len(word)-1):
            for k in range(j+1, len(word)):
                swapped_list = list(reversed_word)
                swapped_list[j], swapped_list[k] = (swapped_list[k],
                                                    swapped_list[j])
                swapped_word = ''.join(swapped_list)
                if swapped_word in seen_words:
                    score = k - j
                    ends = (0, len(word)-1)
                    if j in ends:
                        score *= 2
                    if k in ends:
                        score *= 2
                    display_word = swapped_word
                    if display_word == display_word[::-1]:
                        #Palindrome, use other word
                        display_word = word
                    if display_word == display_word[::-1]:
                        #Both palindromes, give up.
                        continue
                    if display_word == 'sales':
                        print frequency_score, word, swapped_word
                    yield (score, frequency_score, display_word)

    def find_bacronyms(self, sorted_words):
        seen_words = set()
        self.bacronyms = []
        barconyms = [] #[(score, frequency_score, word)]
        print 'Starting...'
        banned_words = set("shad septa girt".split())
        for frequency_score, word in enumerate(sorted_words):
            if len(word) < 4 or word in banned_words:
                continue
            reversed_word = word[::-1]
            if reversed_word in seen_words:
                self.bacronyms.append(reversed_word)
            else:
                barconyms.extend(self.find_barconyms(word,
                                                     reversed_word,
                                                     frequency_score,
                                                     seen_words,
                                                     1))
                seen_words.add(word)

        barconyms.sort()
        self.barconyms = []
        for _score, _frequency_score, word in barconyms:
            if (word not in self.bacronyms and
                word not in self.barconyms):

                self.barconyms.append(word)
        print len(self.bacronyms), len(self.barconyms)
        output_count = min(len(self.bacronyms), len(self.barconyms)/2)
        for i in range(output_count):
            words = {self.bacronyms[i],
                     self.barconyms[i],
                     self.barconyms[i+output_count]}
            print ' '.join(words)

if __name__ == '__main__':
    loader = WordLoader()
    sorted_words_filename = 'sorted_words.txt'
    if os.path.isfile(sorted_words_filename):
        loader.load_sorted_words(sorted_words_filename)
    else:
        loader.load_valid_words('/usr/share/dict/british-english')
        loader.load_valid_words('/usr/share/dict/american-english')
        all_words = brown.words() + gutenberg.words()
        loader.write_sorted_words(all_words, sorted_words_filename)

    loader.find_bacronyms(loader.sorted_words)
#     loader.dump_words(all_words)
#    loader.print_sandwiches(all_words)
#     f = open("/home/don/Documents/RussianDolls2.txt")
#     try:
#         loader.filter_sandwiches(f, all_words)
#     finally:
#         f.close()
elif __name__ == '__live_coding__':
    loader = WordLoader()
    is_valid = loader.is_valid('potato')
    loader.load_valid_words('sample.txt')
    loader.load_valid_words('sample2.txt')
    loader._valid_words = set("net ten dear read lengthy pear reap mean name amen".split())

    all_words = "net ten ten dear dear read read raed lengthy pear reap mean name name amen".split()
    loader.write_sorted_words(all_words, 'sample3.txt')

#     loader.dump_words(all_words)
#     loader.print_sandwiches(all_words)
    sorted_words = 'deified edified mood dread west doom dared stew deer reed dear read lengthy name mean'.split()
    lines = ['1e-5 plain coming\n',
             '1e-6 ot a\n']
    loader.find_bacronyms(sorted_words)
<|endoftext|>


<commit_before>"""Highlevel API for managing PRs on Github"""

import abc
import logging

from copy import copy

from gidgethub.abc import GitHubAPI
from gidgethub import aiohttp as gh_aiohttp
import aiohttp

from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)  # pylint: disable=invalid-name

class GitHubHandler:
    """Handles interaction with GitHub

    Arguments:
      token: OAUTH token granting permissions to GH
      dry_run: Don't actually modify things if set
      to_user: Target User/Org for PRs
      to_repo: Target repository within **to_user**
    """
    USER_AGENT = "bioconda/bioconda-utils"
    PULLS = "/repos/{user}/{repo}/pulls{/number}{?head,base}"
    ISSUES = "/repos/{user}/{repo}/issues{/number}"

    def __init__(self, token: str,
                 dry_run: bool = False,
                 to_user: str = "bioconda",
                 to_repo: str = "bioconnda-recipes") -> None:
        self.token = token
        self.dry_run = dry_run
        self.var_default = {'user': to_user,
                            'repo': to_repo}

        # filled in by login():
        self.api: GitHubAPI = None
        self.username: str = None

    @abc.abstractmethod
    def create_api_object(self, *args, **kwargs):
        """Create API object"""

    async def login(self, *args, **kwargs):
        """Log into API (fills `self.username`)"""

        self.create_api_object(*args, **kwargs)

        user = await self.api.getitem("/user")
        self.username = user["login"]

    async def is_member(self, username):
        """Tests if **username** is a member"""
        # FIXME unfinished
        res = await self.gh.getitem("/orgs/:org/members/:username")

    async def get_prs(self,
                      from_branch: Optional[str] = None,
                      from_user: Optional[str] = None,
                      to_branch: Optional[str] = None,
                      number: Optional[int] = None) -> List[Dict[Any, Any]]:
        """Retrieve list of PRs matching parameters

        Arguments:
          from_branch: Name of branch from which PR asks to pull
          from_user: Name of user/org in from which to pull
                     (default: from auth)
          to_branch: Name of branch into which to pull (default: master)
          number: PR number
        """
        var_data = copy(self.var_default)
        if not from_user:
            from_user = self.username
        if from_branch:
            if from_user:
                var_data['head'] = f"{from_user}:{from_branch}"
            else:
                var_data['head'] = from_branch
        if to_branch:
            var_data['base'] = to_branch
        if number:
            var_data['number'] = str(number)

        return await self.api.getitem(self.PULLS, var_data)

    async def create_pr(self, title: str,
                        from_branch: Optional[str] = None,
                        from_user: Optional[str] = None,
                        to_branch: Optional[str] = "master",
                        body: Optional[str] = None,
                        maintainer_can_modify: bool = True) -> Dict[Any, Any]:
        """Create new PR

        Arguments:
          title: Title of new PR
          from_branch: Name of branch from which PR asks to pull
          from_user: Name of user/org in from which to pull
          to_branch: Name of branch into which to pull (default: master)
          body: Body text of PR
          maintainer_can_modify: Whether to allow maintainer to modify from_branch
        """
        var_data = copy(self.var_default)
        if not from_user:
            from_user = self.username
        data: Dict[str, Any] = {'title': title,
                                'body': '',
                                'maintainer_can_modify': maintainer_can_modify}
        if body:
            data['body'] += body
        if from_branch:
            if from_user:
                data['head'] = f"{from_user}:{from_branch}"
            else:
                data['head'] = from_branch
        if to_branch:
            data['base'] = to_branch

        logger.debug("PR data %s", data)
        if self.dry_run:
            logger.info("Would create PR '%s'", title)
            return {'number': -1}
        logger.info("Creating PR '%s'", title)
        return await self.api.post(self.PULLS, var_data, data=data)

    async def modify_issue(self, number: int,
                           labels: Optional[List[str]] = None,
                           title: Optional[str] = None,
                           body: Optional[str] = None) -> Dict[Any, Any]:
        """Modify existing issue (PRs are issues)

        Arguments:
          labels: list of labels to assign to issue
          title: new title
          body: new body
        """
        var_data = copy(self.var_default)
        var_data["number"] = str(number)
        data: Dict[str, Any] = {}
        if labels:
            data['labels'] = labels
        if title:
            data['title'] = title
        if body:
            data['body'] = body

        if self.dry_run:
            logger.info("Would modify PR %s", number)
            return {'number': number}
        logger.info("Modifying PR %s", number)
        return await self.api.patch(self.ISSUES, var_data, data=data)

class AiohttpGitHubHandler(GitHubHandler):
    """GitHubHandler using Aiohttp for HTTP requests

    Arguments:
      session: Aiohttp Client Session object
    """
    def create_api_object(self, session: aiohttp.ClientSession, *args, **kwargs):
        self.api = gh_aiohttp.GitHubAPI(session,
                                        self.USER_AGENT,
                                        oauth_token=self.token)
<commit_msg>Add ability to list closed or all PRs to githubhandler<commit_after>"""Highlevel API for managing PRs on Github"""

import abc
import logging

from copy import copy
from enum import Enum

from gidgethub.abc import GitHubAPI
from gidgethub import aiohttp as gh_aiohttp
import aiohttp

from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)  # pylint: disable=invalid-name

#: State for Github Issues
IssueState = Enum("IssueState", "open closed all")

class GitHubHandler:
    """Handles interaction with GitHub

    Arguments:
      token: OAUTH token granting permissions to GH
      dry_run: Don't actually modify things if set
      to_user: Target User/Org for PRs
      to_repo: Target repository within **to_user**
    """
    USER_AGENT = "bioconda/bioconda-utils"
    PULLS = "/repos/{user}/{repo}/pulls{/number}{?head,base,state}"
    ISSUES = "/repos/{user}/{repo}/issues{/number}"

    STATE = IssueState

    def __init__(self, token: str,
                 dry_run: bool = False,
                 to_user: str = "bioconda",
                 to_repo: str = "bioconnda-recipes") -> None:
        self.token = token
        self.dry_run = dry_run
        self.var_default = {'user': to_user,
                            'repo': to_repo}

        # filled in by login():
        self.api: GitHubAPI = None
        self.username: str = None

    @abc.abstractmethod
    def create_api_object(self, *args, **kwargs):
        """Create API object"""

    async def login(self, *args, **kwargs):
        """Log into API (fills `self.username`)"""

        self.create_api_object(*args, **kwargs)

        user = await self.api.getitem("/user")
        self.username = user["login"]

    async def is_member(self, username):
        """Tests if **username** is a member"""
        # FIXME unfinished
        res = await self.gh.getitem("/orgs/:org/members/:username")

    async def get_prs(self,
                      from_branch: Optional[str] = None,
                      from_user: Optional[str] = None,
                      to_branch: Optional[str] = None,
                      number: Optional[int] = None,
                      state: Optional[IssueState] = None) -> List[Dict[Any, Any]]:
        """Retrieve list of PRs matching parameters

        Arguments:
          from_branch: Name of branch from which PR asks to pull
          from_user: Name of user/org in from which to pull
                     (default: from auth)
          to_branch: Name of branch into which to pull (default: master)
          number: PR number
        """
        var_data = copy(self.var_default)
        if not from_user:
            from_user = self.username
        if from_branch:
            if from_user:
                var_data['head'] = f"{from_user}:{from_branch}"
            else:
                var_data['head'] = from_branch
        if to_branch:
            var_data['base'] = to_branch
        if number:
            var_data['number'] = str(number)
        if state:
            var_data['state'] = state.name.lower()

        return await self.api.getitem(self.PULLS, var_data)

    async def create_pr(self, title: str,
                        from_branch: Optional[str] = None,
                        from_user: Optional[str] = None,
                        to_branch: Optional[str] = "master",
                        body: Optional[str] = None,
                        maintainer_can_modify: bool = True) -> Dict[Any, Any]:
        """Create new PR

        Arguments:
          title: Title of new PR
          from_branch: Name of branch from which PR asks to pull
          from_user: Name of user/org in from which to pull
          to_branch: Name of branch into which to pull (default: master)
          body: Body text of PR
          maintainer_can_modify: Whether to allow maintainer to modify from_branch
        """
        var_data = copy(self.var_default)
        if not from_user:
            from_user = self.username
        data: Dict[str, Any] = {'title': title,
                                'body': '',
                                'maintainer_can_modify': maintainer_can_modify}
        if body:
            data['body'] += body
        if from_branch:
            if from_user:
                data['head'] = f"{from_user}:{from_branch}"
            else:
                data['head'] = from_branch
        if to_branch:
            data['base'] = to_branch

        logger.debug("PR data %s", data)
        if self.dry_run:
            logger.info("Would create PR '%s'", title)
            return {'number': -1}
        logger.info("Creating PR '%s'", title)
        return await self.api.post(self.PULLS, var_data, data=data)

    async def modify_issue(self, number: int,
                           labels: Optional[List[str]] = None,
                           title: Optional[str] = None,
                           body: Optional[str] = None) -> Dict[Any, Any]:
        """Modify existing issue (PRs are issues)

        Arguments:
          labels: list of labels to assign to issue
          title: new title
          body: new body
        """
        var_data = copy(self.var_default)
        var_data["number"] = str(number)
        data: Dict[str, Any] = {}
        if labels:
            data['labels'] = labels
        if title:
            data['title'] = title
        if body:
            data['body'] = body

        if self.dry_run:
            logger.info("Would modify PR %s", number)
            return {'number': number}
        logger.info("Modifying PR %s", number)
        return await self.api.patch(self.ISSUES, var_data, data=data)

class AiohttpGitHubHandler(GitHubHandler):
    """GitHubHandler using Aiohttp for HTTP requests

    Arguments:
      session: Aiohttp Client Session object
    """
    def create_api_object(self, session: aiohttp.ClientSession, *args, **kwargs):
        self.api = gh_aiohttp.GitHubAPI(session,
                                        self.USER_AGENT,
                                        oauth_token=self.token)
<|endoftext|>


<commit_before>from __future__ import unicode_literals

from email import encoders
from email.mime.base import MIMEBase
import os

from rfc6266 import build_header
import six

from django.conf import settings
from django.core.files.uploadhandler import FileUploadHandler, SkipFile

from modoboa.lib.exceptions import InternalError
from modoboa.lib.web_utils import size2integer
from modoboa.parameters import tools as param_tools

def set_compose_session(request):
    """Initialize a new "compose" session.

    It is used to keep track of attachments defined with a new
    message. Each new message will be associated with a unique ID (in
    order to avoid conflicts between users).

    :param request: a Request object.
    :return: the new unique ID.
    """
    import uuid
    randid = str(uuid.uuid4()).replace("-", "")
    request.session["compose_mail"] = {"id": randid, "attachments": []}
    return randid

def save_attachment(f):
    """Save a new attachment to the filesystem.

    The attachment is not saved using its own name to the
    filesystem. To avoid conflicts, a random name is generated and
    used instead.

    :param f: an uploaded file object (see Django's documentation)
    :return: the new random name
    """
    from tempfile import NamedTemporaryFile

    dstdir = os.path.join(settings.MEDIA_ROOT, "webmail")
    try:
        fp = NamedTemporaryFile(dir=dstdir, delete=False)
    except Exception as e:
        raise InternalError(str(e))
    for chunk in f.chunks():
        fp.write(chunk)
    fp.close()
    return fp.name

def clean_attachments(attlist):
    """Remove all attachments from the filesystem

    :param attlist: a list of 2-uple. Each element must contain the
                    following information : (random name, real name).
    """
    for att in attlist:
        fullpath = os.path.join(settings.MEDIA_ROOT, "webmail", att["tmpname"])
        try:
            os.remove(fullpath)
        except OSError:
            pass

def create_mail_attachment(attdef, payload=None):
    """Create the MIME part corresponding to the given attachment.

    Mandatory keys: 'fname', 'tmpname', 'content-type'

    :param attdef: a dictionary containing the attachment definition
    :return: a MIMEBase object
    """
    if "content-type" in attdef:
        maintype, subtype = attdef["content-type"].split("/")
    elif "Content-Type" in attdef:
        maintype, subtype = attdef["Content-Type"].split("/")
    else:
        return None
    res = MIMEBase(maintype, subtype)
    if payload is None:
        with open(os.path.join(
                settings.MEDIA_ROOT, "webmail", attdef["tmpname"]),
                  "rb") as fp:
            res.set_payload(fp.read())
    else:
        res.set_payload(payload)
    encoders.encode_base64(res)
    if isinstance(attdef["fname"], six.binary_type):
        attdef["fname"] = attdef["fname"].decode("utf-8")
    res["Content-Disposition"] = build_header(attdef["fname"])
    return res

class AttachmentUploadHandler(FileUploadHandler):

    """
    Simple upload handler to limit the size of the attachments users
    can upload.
    """

    def __init__(self, request=None):
        super(AttachmentUploadHandler, self).__init__(request)
        self.total_upload = 0
        self.toobig = False
        self.maxsize = size2integer(
            param_tools.get_global_parameter("max_attachment_size"))

    def receive_data_chunk(self, raw_data, start):
        self.total_upload += len(raw_data)
        if self.total_upload >= self.maxsize:
            self.toobig = True
            raise SkipFile()
        return raw_data

    def file_complete(self, file_size):
        return None
<commit_msg>Fix crash when sending attachments with non ascii filename with python 3<commit_after>from __future__ import unicode_literals

from email import encoders
from email.mime.base import MIMEBase
import os

from rfc6266 import build_header
import six

from django.conf import settings
from django.core.files.uploadhandler import FileUploadHandler, SkipFile

from modoboa.lib.exceptions import InternalError
from modoboa.lib.web_utils import size2integer
from modoboa.parameters import tools as param_tools

def set_compose_session(request):
    """Initialize a new "compose" session.

    It is used to keep track of attachments defined with a new
    message. Each new message will be associated with a unique ID (in
    order to avoid conflicts between users).

    :param request: a Request object.
    :return: the new unique ID.
    """
    import uuid
    randid = str(uuid.uuid4()).replace("-", "")
    request.session["compose_mail"] = {"id": randid, "attachments": []}
    return randid

def save_attachment(f):
    """Save a new attachment to the filesystem.

    The attachment is not saved using its own name to the
    filesystem. To avoid conflicts, a random name is generated and
    used instead.

    :param f: an uploaded file object (see Django's documentation)
    :return: the new random name
    """
    from tempfile import NamedTemporaryFile

    dstdir = os.path.join(settings.MEDIA_ROOT, "webmail")
    try:
        fp = NamedTemporaryFile(dir=dstdir, delete=False)
    except Exception as e:
        raise InternalError(str(e))
    for chunk in f.chunks():
        fp.write(chunk)
    fp.close()
    return fp.name

def clean_attachments(attlist):
    """Remove all attachments from the filesystem

    :param attlist: a list of 2-uple. Each element must contain the
                    following information : (random name, real name).
    """
    for att in attlist:
        fullpath = os.path.join(settings.MEDIA_ROOT, "webmail", att["tmpname"])
        try:
            os.remove(fullpath)
        except OSError:
            pass

def create_mail_attachment(attdef, payload=None):
    """Create the MIME part corresponding to the given attachment.

    Mandatory keys: 'fname', 'tmpname', 'content-type'

    :param attdef: a dictionary containing the attachment definition
    :return: a MIMEBase object
    """
    if "content-type" in attdef:
        maintype, subtype = attdef["content-type"].split("/")
    elif "Content-Type" in attdef:
        maintype, subtype = attdef["Content-Type"].split("/")
    else:
        return None
    res = MIMEBase(maintype, subtype)
    if payload is None:
        with open(os.path.join(
                settings.MEDIA_ROOT, "webmail", attdef["tmpname"]),
                  "rb") as fp:
            res.set_payload(fp.read())
    else:
        res.set_payload(payload)
    encoders.encode_base64(res)
    if isinstance(attdef["fname"], six.binary_type):
        attdef["fname"] = attdef["fname"].decode("utf-8")
    content_disposition = build_header(attdef["fname"])
    if isinstance(content_disposition, six.binary_type):
        res["Content-Disposition"] = content_disposition.decode("utf-8")
    else:
        res["Content-Disposition"] = content_disposition
    return res

class AttachmentUploadHandler(FileUploadHandler):

    """
    Simple upload handler to limit the size of the attachments users
    can upload.
    """

    def __init__(self, request=None):
        super(AttachmentUploadHandler, self).__init__(request)
        self.total_upload = 0
        self.toobig = False
        self.maxsize = size2integer(
            param_tools.get_global_parameter("max_attachment_size"))

    def receive_data_chunk(self, raw_data, start):
        self.total_upload += len(raw_data)
        if self.total_upload >= self.maxsize:
            self.toobig = True
            raise SkipFile()
        return raw_data

    def file_complete(self, file_size):
        return None
<|endoftext|>


<commit_before>#!/usr/bin/python
"""reports for processing."""

import argparse
import sqlite3
import uuid
import store

# key/vals
OUT_PACKETS = 'Acct-Output-Packets'
IN_PACKETS = 'Acct-Input-Packets'
IN_OCTET = "Acct-Input-Octets"
OUT_OCTET = 'Acct-Output-Octets'
ACCT_SESS_TIME = "Acct-Session-Time"

# queries
USERS_BY_KEY = """
    select key as attr, val datum, user, date
    from data inner join users on users.line = data.line
    where key in ({0})
"""
ACCOUNTING_SUM = """
    select user, date, attr, sum(datum) as datum
    from ({0}) group by user, date, attr
"""
ACCOUNTING_SUM_AGGR = """
    select user, attr, sum(datum) as datum from ({0}) group by user, attr
"""
AUTHORIZES = """
    select user, date, sum(authorizes) as total
    from (
        select date, user, 1 as authorizes
        from data
        inner join users on users.line = data.line
        where type = 'AUTHORIZE'
        and key = '{0}'
    ) as X
    group by date, user order by date, user
"""
AUTHORIZES_AGGR = """
    select user, sum(total) as total from ({0}) as Z group by user
"""
SESSION_TIME_STATS = """
    select date,
    user,
    round(avg(datum)) as avg_val,
    max(datum) as max_val,
    min(datum) as min_val,
    sum(datum) as sum_val
    from ({0}) as Y
    group by date, user order by date, user
"""
SESSION_TIME_AGGR = """
    select user,
           round(avg(avg_val)) as avg_val,
           max(max_val) as max_val,
           min(min_val) as min_val,
           sum(sum_val) as sum_val
           from ({0}) as Z
           group by user
"""
USER_MACS = """
    select max(date) as date, user, mac from
    (
        select date, user, mac
        from data
        inner join users on users.line = data.line
        inner join macs on macs.line = data.line
        where key = '{0}'
    ) group by user, mac order by date
"""

USER_MAC_REPORT = """
    select user, mac from
    ({0})
    group by user, mac
    order by user, mac
"""

ALL_USERS = """
    select user, max(date) as date
    from (
        select user, date from data
        inner join users on users.line = data.line
        where key = '{0}'
        order by date desc
    ) group by user order by date
""".format(store.USER_NAME)

ALL_USERS_REPORT = "select user from ({0}) group by user"

def _packets_daily(cursor, opts):
    """packet report daily."""
    _packets(cursor, False, opts)

def _packets_all(cursor, opts):
    """packet report all."""
    _packets(cursor, True, opts)

def _octets_all(cursor, opts):
    """octet report all."""
    _octets(cursor, True, opts)

def _octets_daily(cursor, opts):
    """octet report daily."""
    _octets(cursor, False, opts)

def _session_time_all(cursor, opts):
    """session time all."""
    _session_time(cursor, True, opts)

def _session_time_daily(cursor, opts):
    """session time daily."""
    _session_time(cursor, False, opts)

def _authorizes_all(cursor, opts):
    """all authorizes."""
    _authorizes(cursor, True, opts)

def _authorizes_daily(cursor, opts):
    """daily authorizes."""
    _authorizes(cursor, False, opts)

def _packets(cursor, aggr, opts):
    """print information about packet throughput."""
    _accounting_stat(cursor, IN_PACKETS, OUT_PACKETS, aggr, opts)

def _octets(cursor, aggr, opts):
    """print information about octet throughput."""
    _accounting_stat(cursor, IN_OCTET, OUT_OCTET, aggr, opts)

def _print_data(cat, curs, opts):
    """output data."""
    print
    cols = _get_cols(curs)

    def _gen():
        for row in curs.fetchall():
            yield row
    format_str = []
    for col in cols:
        use_format = "15"
        if col == "user":
            use_format = "25"
        if col == "attr":
            use_format = "25"
        if col == "mac":
            use_format = "20"
        format_str.append("{:>" + use_format + "}")
    join_fmt = ""
    if opts.markdown:
        join_fmt = " | "
    formatter = join_fmt.join(format_str)
    if opts.markdown:
        formatter = "| " + formatter + " |"
        print formatter.format(*cols)
        print formatter.format(*["-" for x in cols])
    else:
        print "{0} - ({1})".format(cat, ", ".join(cols))
        print "==="
    for row in _gen():
        print formatter.format(*row)
    print

def _get_cols(cursor):
    """get column names."""
    return list(map(lambda x: x[0], cursor.description))

def _session_time(cursor, aggr, opts):
    """print information about session time."""
    query = USERS_BY_KEY.format("'" + ACCT_SESS_TIME + "'")
    query = SESSION_TIME_STATS.format(query)
    if aggr:
        query = SESSION_TIME_AGGR.format(query)
    cursor.execute(query)
    _print_data("sessions", cursor, opts)

def _authorizes(cursor, aggr, opts):
    """get the number of authorizes by user by day."""
    query = AUTHORIZES.format(store.USER_NAME)
    if aggr:
        query = AUTHORIZES_AGGR.format(query)
    cursor.execute(query)
    _print_data("authorizes", cursor, opts)

def _accounting_stat(cursor, in_col, out_col, aggr, opts):
    """accounting stats."""
    query = USERS_BY_KEY.format("'" + "','".join([out_col, in_col]) + "'")
    query = ACCOUNTING_SUM.format(query)
    if aggr:
        query = ACCOUNTING_SUM_AGGR.format(query)
    cursor.execute(query)
    _print_data("accounting", cursor, opts)

def _user_last(cursor, opts):
    """all user report."""
    _user_last_full(cursor, True, opts)

def _user_last_daily(cursor, opts):
    """user last logged time."""
    _user_last_full(cursor, False, opts)

def _user_last_full(cursor, aggr, opts):
    """user last full query."""
    query = ALL_USERS
    if aggr:
        query = ALL_USERS_REPORT.format(query)
    cursor.execute(query)
    _print_data("users", cursor, opts)

def _user_mac_last_daily(cursor, opts):
    """user+mac last logged time."""
    _user_mac_last(cursor, False, opts)

def _user_mac_full(cursor, opts):
    """user+mac list detected."""
    _user_mac_last(cursor, True, opts)

def _user_mac_last(cursor, aggr, opts):
    """user mac querying."""
    query = USER_MACS.format(store.CALLING_STATION)
    if aggr:
        query = USER_MAC_REPORT.format(query)
    cursor.execute(query)
    _print_data("user/mac report", cursor, opts)

# available reports
available = {}
available["packets"] = _packets_all
available["octets"] = _octets_all
available["authorizes"] = _authorizes_all
available["session-time"] = _session_time_all
available["packets-daily"] = _packets_daily
available["octets-daily"] = _octets_daily
available["authorizes-daily"] = _authorizes_daily
available["session-time-daily"] = _session_time_daily
available["users-daily"] = _user_last_daily
available["users"] = _user_last
available["user-macs-daily"] = _user_mac_last_daily
available["user-macs"] = _user_mac_full

class Options(object):
    """Options for reports."""
    markdown = False

def main():
    """main entry."""
    parser = argparse.ArgumentParser()
    parser.add_argument("--database",
                        required=True,
                        type=str,
                        default=store.DB_NAME)
    parser.add_argument("--reports", nargs='*', choices=available.keys())
    parser.add_argument("--markdown", action="store_true")
    args = parser.parse_args()
    if args.reports is None or len(args.reports) == 0:
        execute = available.keys()
    else:
        execute = args.reports
    conn = sqlite3.connect(args.database)
    curs = conn.cursor()
    opts = Options()
    opts.markdown = args.markdown
    for item in execute:
        if item in available:
            available[item](curs, opts)
        else:
            print "unknown report..." + item
    conn.close()

if __name__ == "__main__":
    main()
<commit_msg>signature output<commit_after>#!/usr/bin/python
"""reports for processing."""

import argparse
import sqlite3
import uuid
import store

# key/vals
OUT_PACKETS = 'Acct-Output-Packets'
IN_PACKETS = 'Acct-Input-Packets'
IN_OCTET = "Acct-Input-Octets"
OUT_OCTET = 'Acct-Output-Octets'
ACCT_SESS_TIME = "Acct-Session-Time"

# queries
USERS_BY_KEY = """
    select key as attr, val datum, user, date
    from data inner join users on users.line = data.line
    where key in ({0})
"""
ACCOUNTING_SUM = """
    select user, date, attr, sum(datum) as datum
    from ({0}) group by user, date, attr
"""
ACCOUNTING_SUM_AGGR = """
    select user, attr, sum(datum) as datum from ({0}) group by user, attr
"""
AUTHORIZES = """
    select user, date, sum(authorizes) as total
    from (
        select date, user, 1 as authorizes
        from data
        inner join users on users.line = data.line
        where type = 'AUTHORIZE'
        and key = '{0}'
    ) as X
    group by date, user order by date, user
"""
AUTHORIZES_AGGR = """
    select user, sum(total) as total from ({0}) as Z group by user
"""
SESSION_TIME_STATS = """
    select date,
    user,
    round(avg(datum)) as avg_val,
    max(datum) as max_val,
    min(datum) as min_val,
    sum(datum) as sum_val
    from ({0}) as Y
    group by date, user order by date, user
"""
SESSION_TIME_AGGR = """
    select user,
           round(avg(avg_val)) as avg_val,
           max(max_val) as max_val,
           min(min_val) as min_val,
           sum(sum_val) as sum_val
           from ({0}) as Z
           group by user
"""
USER_MACS = """
    select max(date) as date, user, mac from
    (
        select date, user, mac
        from data
        inner join users on users.line = data.line
        inner join macs on macs.line = data.line
        where key = '{0}'
    ) group by user, mac order by date
"""

USER_MAC_REPORT = """
    select user, mac from
    ({0})
    group by user, mac
    order by user, mac
"""

ALL_USERS = """
    select user, max(date) as date
    from (
        select user, date from data
        inner join users on users.line = data.line
        where key = '{0}'
        order by date desc
    ) group by user order by date
""".format(store.USER_NAME)

ALL_USERS_REPORT = "select user from ({0}) group by user"

SIGNATURES = """
    select distinct user, mac,
    ifnull(nasipaddr.val, 'n/a') as nip,
    ifnull(nasport.val, 'n/a') as np,
    ifnull(nasporttype.val, 'n/a') as npt
    from data
    inner join users on users.line = data.line
    inner join macs on macs.line = data.line
    left join
    (
        select line, val from data where key = 'NAS-IP-Address'
    ) as nasipaddr
    on nasipaddr.line = data.line
    left join
    (
        select line, val from data where key = 'NAS-Port'
    ) as nasport
    on nasport.line = data.line
    left outer join
    (
        select line, val from data where key = 'NAS-Port-Type'
    ) as nasporttype
    on nasporttype.line = data.line
    order by user, mac, nip, np, npt
"""

def _packets_daily(cursor, opts):
    """packet report daily."""
    _packets(cursor, False, opts)

def _packets_all(cursor, opts):
    """packet report all."""
    _packets(cursor, True, opts)

def _octets_all(cursor, opts):
    """octet report all."""
    _octets(cursor, True, opts)

def _octets_daily(cursor, opts):
    """octet report daily."""
    _octets(cursor, False, opts)

def _session_time_all(cursor, opts):
    """session time all."""
    _session_time(cursor, True, opts)

def _session_time_daily(cursor, opts):
    """session time daily."""
    _session_time(cursor, False, opts)

def _authorizes_all(cursor, opts):
    """all authorizes."""
    _authorizes(cursor, True, opts)

def _authorizes_daily(cursor, opts):
    """daily authorizes."""
    _authorizes(cursor, False, opts)

def _packets(cursor, aggr, opts):
    """print information about packet throughput."""
    _accounting_stat(cursor, IN_PACKETS, OUT_PACKETS, aggr, opts)

def _octets(cursor, aggr, opts):
    """print information about octet throughput."""
    _accounting_stat(cursor, IN_OCTET, OUT_OCTET, aggr, opts)

def _print_data(cat, curs, opts, converter=None):
    """output data."""
    print
    if converter is None:
        cols = _get_cols(curs)
    else:
        cols = converter(None, True)

    def _gen():
        for row in curs.fetchall():
            if converter is None:
                yield row
            else:
                row_res = converter(row, False)
                if row_res:
                    yield row_res
    format_str = []
    for col in cols:
        use_format = "15"
        if col in ["attr", "user"]:
            use_format = "25"
        if col == "signature":
            use_format = "50"
        if col == "mac":
            use_format = "20"
        format_str.append("{:>" + use_format + "}")
    join_fmt = ""
    if opts.markdown:
        join_fmt = " | "
    formatter = join_fmt.join(format_str)
    if opts.markdown:
        formatter = "| " + formatter + " |"
        print formatter.format(*cols)
        print formatter.format(*["-" for x in cols])
    else:
        print "{0} - ({1})".format(cat, ", ".join(cols))
        print "==="
    for row in _gen():
        print formatter.format(*row)
    print

def _get_cols(cursor):
    """get column names."""
    return list(map(lambda x: x[0], cursor.description))

def _session_time(cursor, aggr, opts):
    """print information about session time."""
    query = USERS_BY_KEY.format("'" + ACCT_SESS_TIME + "'")
    query = SESSION_TIME_STATS.format(query)
    if aggr:
        query = SESSION_TIME_AGGR.format(query)
    cursor.execute(query)
    _print_data("sessions", cursor, opts)

def _authorizes(cursor, aggr, opts):
    """get the number of authorizes by user by day."""
    query = AUTHORIZES.format(store.USER_NAME)
    if aggr:
        query = AUTHORIZES_AGGR.format(query)
    cursor.execute(query)
    _print_data("authorizes", cursor, opts)

def _accounting_stat(cursor, in_col, out_col, aggr, opts):
    """accounting stats."""
    query = USERS_BY_KEY.format("'" + "','".join([out_col, in_col]) + "'")
    query = ACCOUNTING_SUM.format(query)
    if aggr:
        query = ACCOUNTING_SUM_AGGR.format(query)
    cursor.execute(query)
    _print_data("accounting", cursor, opts)

def _user_last(cursor, opts):
    """all user report."""
    _user_last_full(cursor, True, opts)

def _user_last_daily(cursor, opts):
    """user last logged time."""
    _user_last_full(cursor, False, opts)

def _user_last_full(cursor, aggr, opts):
    """user last full query."""
    query = ALL_USERS
    if aggr:
        query = ALL_USERS_REPORT.format(query)
    cursor.execute(query)
    _print_data("users", cursor, opts)

def _user_mac_last_daily(cursor, opts):
    """user+mac last logged time."""
    _user_mac_last(cursor, False, opts)

def _user_mac_full(cursor, opts):
    """user+mac list detected."""
    _user_mac_last(cursor, True, opts)

def _user_mac_last(cursor, aggr, opts):
    """user mac querying."""
    query = USER_MACS.format(store.CALLING_STATION)
    if aggr:
        query = USER_MAC_REPORT.format(query)
    cursor.execute(query)
    _print_data("user/mac report", cursor, opts)

def _signatures(cursor, opts):
    """signature output."""
    cursor.execute(SIGNATURES)

    def _conv(row, get_columns):
        if get_columns:
            return ["user", "mac", "signature"]
        else:
            return [row[0], row[1], ":::".join(row[2:])]
    _print_data("signatures", cursor, opts, converter=_conv)

# available reports
available = {}
available["packets"] = _packets_all
available["octets"] = _octets_all
available["authorizes"] = _authorizes_all
available["session-time"] = _session_time_all
available["packets-daily"] = _packets_daily
available["octets-daily"] = _octets_daily
available["authorizes-daily"] = _authorizes_daily
available["session-time-daily"] = _session_time_daily
available["users-daily"] = _user_last_daily
available["users"] = _user_last
available["user-macs-daily"] = _user_mac_last_daily
available["user-macs"] = _user_mac_full
available["signatures"] = _signatures

class Options(object):
    """Options for reports."""

    markdown = False

def main():
    """main entry."""
    parser = argparse.ArgumentParser()
    parser.add_argument("--database",
                        required=True,
                        type=str,
                        default=store.DB_NAME)
    parser.add_argument("--reports", nargs='*', choices=available.keys())
    parser.add_argument("--markdown", action="store_true")
    args = parser.parse_args()
    if args.reports is None or len(args.reports) == 0:
        execute = available.keys()
    else:
        execute = args.reports
    conn = sqlite3.connect(args.database)
    curs = conn.cursor()
    opts = Options()
    opts.markdown = args.markdown
    for item in execute:
        if item in available:
            available[item](curs, opts)
        else:
            print "unknown report..." + item
    conn.close()

if __name__ == "__main__":
    main()
<|endoftext|>


<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (C) 2009-2012:
#    Gabes Jean, naparuba@gmail.com
#    Gerhard Lausser, Gerhard.Lausser@consol.de
#    Gregory Starck, g.starck@gmail.com
#    Hartmut Goebel, h.goebel@goebel-consult.de
#
# This file is part of Shinken.
#
# Shinken 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.
#
# Shinken 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Shinken.  If not, see <http://www.gnu.org/licenses/>.

from livestatus_query import LiveStatusQuery
from shinken.external_command import ExternalCommand
from shinken.log import logger

class LiveStatusCommandQuery(LiveStatusQuery):

    my_type = 'command'

    def parse_input(self, data):
        """Parse the lines of a livestatus request.

        This function looks for keywords in input lines and
        sets the attributes of the request object

        """
        for line in data.splitlines():
            line = line.strip()
            # Tools like NagVis send KEYWORK:option, and we prefer to have
            # a space following the:
            if ':' in line and not ' ' in line:
                line = line.replace(':', ': ')
            keyword = line.split(' ')[0].rstrip(':')
            if keyword == 'COMMAND':
                _, self.extcmd = line.split(' ', 1)
            else:
                # This line is not valid or not implemented
                logger.warning("[Livestatus Broker Command Query] Received a line of input which i can't handle: '%s'" % line)
                pass

    def launch_query(self):
        """ Prepare the request object's filter stacks """

        # The Response object needs to access the Query
        self.response.load(self)

        if self.extcmd:
            # External command are send back to broker

            # Somehow this line seems to prevent us from sending external commands from THruk with accents.
            #self.extcmd = self.extcmd.decode('utf8', 'replace')
            e = ExternalCommand(self.extcmd)
            self.return_queue.put(e)
            return []
<commit_msg>PEP8: livestatus_command_query<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (C) 2009-2012:
#    Gabes Jean, naparuba@gmail.com
#    Gerhard Lausser, Gerhard.Lausser@consol.de
#    Gregory Starck, g.starck@gmail.com
#    Hartmut Goebel, h.goebel@goebel-consult.de
#
# This file is part of Shinken.
#
# Shinken 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.
#
# Shinken 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Shinken.  If not, see <http://www.gnu.org/licenses/>.

from livestatus_query import LiveStatusQuery
from shinken.external_command import ExternalCommand
from shinken.log import logger

class LiveStatusCommandQuery(LiveStatusQuery):

    my_type = 'command'

    def parse_input(self, data):
        """Parse the lines of a livestatus request.

        This function looks for keywords in input lines and
        sets the attributes of the request object

        """
        for line in data.splitlines():
            line = line.strip()
            # Tools like NagVis send KEYWORK:option, and we prefer to have
            # a space following the:
            if ':' in line and ' ' not in line:
                line = line.replace(':', ': ')
            keyword = line.split(' ')[0].rstrip(':')
            if keyword == 'COMMAND':
                _, self.extcmd = line.split(' ', 1)
            else:
                # This line is not valid or not implemented
                logger.warning("[Livestatus Broker Command Query] Received a line of input "
                               "which i can't handle: '%s'" % line)
                pass

    def launch_query(self):
        """ Prepare the request object's filter stacks """

        # The Response object needs to access the Query
        self.response.load(self)

        if self.extcmd:
            # External command are send back to broker
            # TODO: check and clean this:
            # Somehow this line seems to prevent us from sending external
            # commands from THruk with accents:
            # self.extcmd = self.extcmd.decode('utf8', 'replace')
            e = ExternalCommand(self.extcmd)
            self.return_queue.put(e)
            return []
<|endoftext|>


<commit_before># Copyright 2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

{
    'name': 'Module Auto Update',
    'summary': 'Automatically update Odoo modules',
    'version': '11.0.2.0.3',
    'category': 'Extra Tools',
    'website': 'https://github.com/OCA/server-tools',
    'author': 'LasLabs, '
              'Juan José Scarafía, '
              'Tecnativa, '
              'ACSONE SA/NV, '
              'Odoo Community Association (OCA)',
    'license': 'LGPL-3',
    'application': False,
    'installable': True,
    'uninstall_hook': 'uninstall_hook',
    'depends': [
        'base',
    ],
    'data': [
        'data/cron_data_deprecated.xml',
    ],
}
<commit_msg>module_auto_update: sbidoul as maintainer<commit_after># Copyright 2017 LasLabs Inc.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

{
    'name': 'Module Auto Update',
    'summary': 'Automatically update Odoo modules',
    'version': '11.0.2.0.3',
    'category': 'Extra Tools',
    'website': 'https://github.com/OCA/server-tools',
    'author': 'LasLabs, '
              'Juan José Scarafía, '
              'Tecnativa, '
              'ACSONE SA/NV, '
              'Odoo Community Association (OCA)',
    'license': 'LGPL-3',
    'application': False,
    'installable': True,
    'uninstall_hook': 'uninstall_hook',
    'depends': [
        'base',
    ],
    'data': [
        'data/cron_data_deprecated.xml',
    ],
    'development_status': 'Production/Stable',
    'maintainers': ['sbidoul'],
}
<|endoftext|>