<commit_before># -*- coding: utf-8 -*-
import sys
import os
import hashlib
import tempfile
from collections import namedtuple

import delegator
import pip
import parse
import requirements
import requests
import six

from piptools.resolver import Resolver
from piptools.repositories.pypi import PyPIRepository
from piptools.scripts.compile import get_pip_command
from piptools import logging

# List of version control systems we support.
VCS_LIST = ('git', 'svn', 'hg', 'bzr')
FILE_LIST = ('http://', 'https://', 'ftp://', 'file:///')

requests = requests.session()

def python_version(path_to_python):
    print(path_to_python)

    if not path_to_python:
        return None

    # Quote the path to Python, for Windows.
    path_to_python = shellquote(path_to_python)

    try:
        TEMPLATE = 'Python {}.{}.{}'
        print('{0} --version'.format(path_to_python))
        c = delegator.run('{0} --version'.format(path_to_python), block=False)
        c.return_code == 0
    except Exception as e:
        print(e)
        return None

    print('yay')
    output = c.out.strip() or c.err.strip()
    parsed = parse.parse(TEMPLATE, output).fixed

    return u"{v[0]}.{v[1]}.{v[2]}".format(v=parsed)

def shellquote(s):
    """Prepares a string for the shell (on Windows too!)"""
    return '"' + s.replace("'", "'\\''") + '"'

def clean_pkg_version(version):
    """Uses pip to prepare a package version string, from our internal version."""
    return six.u(pep440_version(str(version).replace('==', '')))

class HackedPythonVersion(object):
    """A Beautiful hack, which allows us to tell pip which version of Python we're using."""
    def __init__(self, python):
        self.python = python
        self.original = sys.version_info

    def __enter__(self):
        if self.python:
            # Create a new named tuple to place fake version info into.
            fake_version_info = namedtuple('fake_version_info', ['major', 'minor', 'micro', 'releaselevel', 'serial'])

            # Parse Python version.
            python = self.python.split('.')

            # Hack sys.version_info to contain our information instead...
            sys.version_info = fake_version_info(int(python[0]), int(python[1]), int(python[2]), 'final', 0)

    def __exit__(self, *args):
        # Restore original Python version information.
        sys.version_info = self.original

def resolve_deps(deps, sources=None, verbose=False, python=False):
    """Given a list of dependencies, return a resolved list of dependencies,
    using pip-tools -- and their hashes, using the warehouse API / pip.
    """

    with HackedPythonVersion(python):

        class PipCommand(pip.basecommand.Command):
            """Needed for pip-tools."""
            name = 'PipCommand'

        constraints = []

        for dep in deps:
            if dep.startswith('-e '):
                constraint = pip.req.InstallRequirement.from_editable(dep[len('-e '):])
            else:
                constraint = pip.req.InstallRequirement.from_line(dep)
            constraints.append(constraint)

        pip_command = get_pip_command()

        pip_args = []

        if sources:
            pip_args.extend(['-i', sources[0]['url']])

        pip_options, _ = pip_command.parse_args(pip_args)

        pypi = PyPIRepository(pip_options=pip_options, session=pip._vendor.requests)

        if verbose:
            logging.log.verbose = True

        resolver = Resolver(constraints=constraints, repository=pypi)
        results = []

        # pre-resolve instead of iterating to avoid asking pypi for hashes of editable packages
        resolved_tree = resolver.resolve()

    for result in resolved_tree:
        name = pep423_name(result.name)
        version = clean_pkg_version(result.specifier)

        collected_hashes = []

        try:
            # Grab the hashes from the new warehouse API.
            r = requests.get('https://pypi.org/pypi/{0}/json'.format(name))
            api_releases = r.json()['releases']

            cleaned_releases = {}
            for api_version, api_info in api_releases.items():
                cleaned_releases[clean_pkg_version(api_version)] = api_info

            for release in cleaned_releases[version]:
                collected_hashes.append(release['digests']['sha256'])

            collected_hashes = ['sha256:' + s for s in collected_hashes]

            # Collect un-collectable hashes.
            if not collected_hashes:
                collected_hashes = list(list(resolver.resolve_hashes([result]).items())[0][1])

        except (ValueError, KeyError):
            pass

        results.append({'name': name, 'version': version, 'hashes': collected_hashes})

    return results

def format_toml(data):
    """Pretty-formats a given toml string."""

    data = data.split('\n')
    for i, line in enumerate(data):
        if i > 0:
            if line.startswith('['):
                data[i] = '\n{0}'.format(line)

    return '\n'.join(data)

def multi_split(s, split):
    """Splits on multiple given separators."""

    for r in split:
        s = s.replace(r, '|')

    return [i for i in s.split('|') if len(i) > 0]

def convert_deps_from_pip(dep):
    """"Converts a pip-formatted dependency to a Pipfile-formatted one."""

    dependency = {}

    req = [r for r in requirements.parse(dep)][0]

    # File installs.
    if req.uri and not req.vcs:

        # Assign a package name to the file, last 7 of it's sha256 hex digest.
        req.name = hashlib.sha256(req.uri.encode('utf-8')).hexdigest()
        req.name = req.name[len(req.name) - 7:]

        # {file: uri} TOML (spec 3 I guess...)
        dependency[req.name] = {'file': req.uri}

    # VCS Installs.
    if req.vcs:
        if req.name is None:
            raise ValueError('pipenv requires an #egg fragment for version controlled '
                             'dependencies. Please install remote dependency '
                             'in the form {0}#egg=<package-name>.'.format(req.uri))

        # Extras: e.g. #egg=requests[security]
        if req.extras:
            dependency[req.name] = {'extras': req.extras}
        # Crop off the git+, etc part.
        dependency.setdefault(req.name, {}).update({req.vcs: req.uri[len(req.vcs) + 1:]})

        # Add --editable, if it's there.
        if req.editable:
            dependency[req.name].update({'editable': True})

        # Add subdirectory, if it's there
        if req.subdirectory:
            dependency[req.name].update({'subdirectory': req.subdirectory})

        # Add the specifier, if it was provided.
        if req.revision:
            dependency[req.name].update({'ref': req.revision})

    elif req.specs or req.extras:

        specs = None
        # Comparison operators: e.g. Django>1.10
        if req.specs:
            r = multi_split(dep, '=<>')
            specs = dep[len(r[0]):]
            dependency[req.name] = specs

        # Extras: e.g. requests[socks]
        if req.extras:
            dependency[req.name] = {'extras': req.extras}

            if specs:
                dependency[req.name].update({'version': specs})

    # Bare dependencies: e.g. requests
    else:
        dependency[dep] = '*'

    return dependency

def convert_deps_to_pip(deps, r=True):
    """"Converts a Pipfile-formatted dependency to a pip-formatted one."""

    dependencies = []

    for dep in deps.keys():

        # Default (e.g. '>1.10').
        extra = deps[dep] if isinstance(deps[dep], six.string_types) else ''
        version = ''

        # Get rid of '*'.
        if deps[dep] == '*' or str(extra) == '{}':
            extra = ''

        hash = ''
        # Support for single hash (spec 1).
        if 'hash' in deps[dep]:
            hash = ' --hash={0}'.format(deps[dep]['hash'])

        # Support for multiple hashes (spec 2).
        if 'hashes' in deps[dep]:
            hash = '{0} '.format(''.join([' --hash={0} '.format(h) for h in deps[dep]['hashes']]))

        # Support for extras (e.g. requests[socks])
        if 'extras' in deps[dep]:
            extra = '[{0}]'.format(deps[dep]['extras'][0])

        if 'version' in deps[dep]:
            version = deps[dep]['version']

        # Support for version control
        maybe_vcs = [vcs for vcs in VCS_LIST if vcs in deps[dep]]
        vcs = maybe_vcs[0] if maybe_vcs else None

        # Support for files.
        if 'file' in deps[dep]:
            dep = deps[dep]['file']

        if vcs:
            extra = '{0}+{1}'.format(vcs, deps[dep][vcs])

            # Support for @refs.
            if 'ref' in deps[dep]:
                extra += '@{0}'.format(deps[dep]['ref'])

            extra += '#egg={0}'.format(dep)

            # Support for subdirectory
            if 'subdirectory' in deps[dep]:
                extra += '&subdirectory={0}'.format(deps[dep]['subdirectory'])

            # Support for editable.
            if 'editable' in deps[dep]:
                # Support for --egg.
                dep = '-e '
            else:
                dep = ''

        dependencies.append('{0}{1}{2}{3}'.format(dep, extra, version, hash))

    if not r:
        return dependencies

    # Write requirements.txt to tmp directory.
    f = tempfile.NamedTemporaryFile(suffix='-requirements.txt', delete=False)
    f.write('\n'.join(dependencies).encode('utf-8'))
    return f.name

def mkdir_p(newdir):
    """works the way a good mkdir should :)
        - already exists, silently complete
        - regular file in the way, raise an exception
        - parent directory(ies) does not exist, make them as well
        From: http://code.activestate.com/recipes/82465-a-friendly-mkdir/
    """

    if os.path.isdir(newdir):
        pass
    elif os.path.isfile(newdir):
        raise OSError("a file with the same name as the desired dir, '{0}', already exists.".format(newdir))
    else:
        head, tail = os.path.split(newdir)
        if head and not os.path.isdir(head):
            mkdir_p(head)
        if tail:
            os.mkdir(newdir)

def is_required_version(version, specified_version):
    """Check to see if there's a hard requirement for version
    number provided in the Pipfile.
    """

    # Certain packages may be defined with multiple values.
    if isinstance(specified_version, dict):
        specified_version = specified_version.get('version', '')
    if specified_version.startswith('=='):
        return version.strip() == specified_version.split('==')[1].strip()
    return True

def is_vcs(pipfile_entry):
    """Determine if dictionary entry from Pipfile is for a vcs dependency."""

    if isinstance(pipfile_entry, dict):
        return any(key for key in pipfile_entry.keys() if key in VCS_LIST)
    return False

def is_file(package):
    """Determine if a package name is for a File dependency."""
    for start in FILE_LIST:
        if package.startswith(start):
            return True

    return False

def pep440_version(version):
    """Normalize version to PEP 440 standards"""

    # Use pip built-in version parser.
    return str(pip.index.parse_version(version))

def pep423_name(name):
    """Normalize package name to PEP 423 style standard."""

    return name.lower().replace('_', '-')

def proper_case(package_name):
    """Properly case project name from pypi.org."""

    # Hit the simple API.
    r = requests.get('https://pypi.org/pypi/{0}/json'.format(package_name), timeout=0.3, stream=True)
    if not r.ok:
        raise IOError('Unable to find package {0} in PyPI repository.'.format(package_name))

    r = parse.parse('https://pypi.org/pypi/{name}/json', r.url)
    good_name = r['name']

    return good_name

def split_vcs(split_file):
    """Split VCS dependencies out from file."""

    if 'packages' in split_file or 'dev-packages' in split_file:
        sections = ('packages', 'dev-packages')
    elif 'default' in split_file or 'develop' in split_file:
        sections = ('default', 'develop')

    # For each vcs entry in a given section, move it to section-vcs.
    for section in sections:
        entries = split_file.get(section, {})
        vcs_dict = dict((k, entries.pop(k)) for k in list(entries.keys()) if is_vcs(entries[k]))
        split_file[section + '-vcs'] = vcs_dict

    return split_file

def recase_file(file_dict):
    """Recase file before writing to output."""

    if 'packages' in file_dict or 'dev-packages' in file_dict:
        sections = ('packages', 'dev-packages')
    elif 'default' in file_dict or 'develop' in file_dict:
        sections = ('default', 'develop')

    for section in sections:
        file_section = file_dict.get(section, {})

        # Try to properly case each key if we can.
        for key in list(file_section.keys()):
            try:
                cased_key = proper_case(key)
            except IOError:
                cased_key = key
            file_section[cased_key] = file_section.pop(key)

    return file_dict

def walk_up(bottom):
    """Mimic os.walk, but walk 'up' instead of down the directory tree.
    From: https://gist.github.com/zdavkeos/1098474
    """

    bottom = os.path.realpath(bottom)

    # Get files in current dir.
    try:
        names = os.listdir(bottom)
    except Exception:
        return

    dirs, nondirs = [], []
    for name in names:
        if os.path.isdir(os.path.join(bottom, name)):
            dirs.append(name)
        else:
            nondirs.append(name)

    yield bottom, dirs, nondirs

    new_path = os.path.realpath(os.path.join(bottom, '..'))

    # See if we are at the top.
    if new_path == bottom:
        return

    for x in walk_up(new_path):
        yield x

def find_requirements(max_depth=3):
    """Returns the path of a Pipfile in parent directories."""

    i = 0
    for c, d, f in walk_up(os.getcwd()):
        i += 1

        if i < max_depth:
            if 'requirements.txt':
                r = os.path.join(c, 'requirements.txt')
                if os.path.isfile(r):
                    return r
    raise RuntimeError('No requirements.txt found!')
<commit_msg>cleanup windows debugging<commit_after># -*- coding: utf-8 -*-
import sys
import os
import hashlib
import tempfile
from collections import namedtuple

import delegator
import pip
import parse
import requirements
import requests
import six

from piptools.resolver import Resolver
from piptools.repositories.pypi import PyPIRepository
from piptools.scripts.compile import get_pip_command
from piptools import logging

# List of version control systems we support.
VCS_LIST = ('git', 'svn', 'hg', 'bzr')
FILE_LIST = ('http://', 'https://', 'ftp://', 'file:///')

requests = requests.session()

def python_version(path_to_python):

    if not path_to_python:
        return None

    # Quote the path to Python, for Windows.
    path_to_python = shellquote(path_to_python)

    try:
        TEMPLATE = 'Python {}.{}.{}'
        c = delegator.run('{0} --version'.format(path_to_python), block=False)
        c.return_code == 0
    except Exception:
        return None

    output = c.out.strip() or c.err.strip()
    parsed = parse.parse(TEMPLATE, output).fixed

    return u"{v[0]}.{v[1]}.{v[2]}".format(v=parsed)

def shellquote(s):
    """Prepares a string for the shell (on Windows too!)"""
    return '"' + s.replace("'", "'\\''") + '"'

def clean_pkg_version(version):
    """Uses pip to prepare a package version string, from our internal version."""
    return six.u(pep440_version(str(version).replace('==', '')))

class HackedPythonVersion(object):
    """A Beautiful hack, which allows us to tell pip which version of Python we're using."""
    def __init__(self, python):
        self.python = python
        self.original = sys.version_info

    def __enter__(self):
        if self.python:
            # Create a new named tuple to place fake version info into.
            fake_version_info = namedtuple('fake_version_info', ['major', 'minor', 'micro', 'releaselevel', 'serial'])

            # Parse Python version.
            python = self.python.split('.')

            # Hack sys.version_info to contain our information instead...
            sys.version_info = fake_version_info(int(python[0]), int(python[1]), int(python[2]), 'final', 0)

    def __exit__(self, *args):
        # Restore original Python version information.
        sys.version_info = self.original

def resolve_deps(deps, sources=None, verbose=False, python=False):
    """Given a list of dependencies, return a resolved list of dependencies,
    using pip-tools -- and their hashes, using the warehouse API / pip.
    """

    with HackedPythonVersion(python):

        class PipCommand(pip.basecommand.Command):
            """Needed for pip-tools."""
            name = 'PipCommand'

        constraints = []

        for dep in deps:
            if dep.startswith('-e '):
                constraint = pip.req.InstallRequirement.from_editable(dep[len('-e '):])
            else:
                constraint = pip.req.InstallRequirement.from_line(dep)
            constraints.append(constraint)

        pip_command = get_pip_command()

        pip_args = []

        if sources:
            pip_args.extend(['-i', sources[0]['url']])

        pip_options, _ = pip_command.parse_args(pip_args)

        pypi = PyPIRepository(pip_options=pip_options, session=pip._vendor.requests)

        if verbose:
            logging.log.verbose = True

        resolver = Resolver(constraints=constraints, repository=pypi)
        results = []

        # pre-resolve instead of iterating to avoid asking pypi for hashes of editable packages
        resolved_tree = resolver.resolve()

    for result in resolved_tree:
        name = pep423_name(result.name)
        version = clean_pkg_version(result.specifier)

        collected_hashes = []

        try:
            # Grab the hashes from the new warehouse API.
            r = requests.get('https://pypi.org/pypi/{0}/json'.format(name))
            api_releases = r.json()['releases']

            cleaned_releases = {}
            for api_version, api_info in api_releases.items():
                cleaned_releases[clean_pkg_version(api_version)] = api_info

            for release in cleaned_releases[version]:
                collected_hashes.append(release['digests']['sha256'])

            collected_hashes = ['sha256:' + s for s in collected_hashes]

            # Collect un-collectable hashes.
            if not collected_hashes:
                collected_hashes = list(list(resolver.resolve_hashes([result]).items())[0][1])

        except (ValueError, KeyError):
            pass

        results.append({'name': name, 'version': version, 'hashes': collected_hashes})

    return results

def format_toml(data):
    """Pretty-formats a given toml string."""

    data = data.split('\n')
    for i, line in enumerate(data):
        if i > 0:
            if line.startswith('['):
                data[i] = '\n{0}'.format(line)

    return '\n'.join(data)

def multi_split(s, split):
    """Splits on multiple given separators."""

    for r in split:
        s = s.replace(r, '|')

    return [i for i in s.split('|') if len(i) > 0]

def convert_deps_from_pip(dep):
    """"Converts a pip-formatted dependency to a Pipfile-formatted one."""

    dependency = {}

    req = [r for r in requirements.parse(dep)][0]

    # File installs.
    if req.uri and not req.vcs:

        # Assign a package name to the file, last 7 of it's sha256 hex digest.
        req.name = hashlib.sha256(req.uri.encode('utf-8')).hexdigest()
        req.name = req.name[len(req.name) - 7:]

        # {file: uri} TOML (spec 3 I guess...)
        dependency[req.name] = {'file': req.uri}

    # VCS Installs.
    if req.vcs:
        if req.name is None:
            raise ValueError('pipenv requires an #egg fragment for version controlled '
                             'dependencies. Please install remote dependency '
                             'in the form {0}#egg=<package-name>.'.format(req.uri))

        # Extras: e.g. #egg=requests[security]
        if req.extras:
            dependency[req.name] = {'extras': req.extras}
        # Crop off the git+, etc part.
        dependency.setdefault(req.name, {}).update({req.vcs: req.uri[len(req.vcs) + 1:]})

        # Add --editable, if it's there.
        if req.editable:
            dependency[req.name].update({'editable': True})

        # Add subdirectory, if it's there
        if req.subdirectory:
            dependency[req.name].update({'subdirectory': req.subdirectory})

        # Add the specifier, if it was provided.
        if req.revision:
            dependency[req.name].update({'ref': req.revision})

    elif req.specs or req.extras:

        specs = None
        # Comparison operators: e.g. Django>1.10
        if req.specs:
            r = multi_split(dep, '=<>')
            specs = dep[len(r[0]):]
            dependency[req.name] = specs

        # Extras: e.g. requests[socks]
        if req.extras:
            dependency[req.name] = {'extras': req.extras}

            if specs:
                dependency[req.name].update({'version': specs})

    # Bare dependencies: e.g. requests
    else:
        dependency[dep] = '*'

    return dependency

def convert_deps_to_pip(deps, r=True):
    """"Converts a Pipfile-formatted dependency to a pip-formatted one."""

    dependencies = []

    for dep in deps.keys():

        # Default (e.g. '>1.10').
        extra = deps[dep] if isinstance(deps[dep], six.string_types) else ''
        version = ''

        # Get rid of '*'.
        if deps[dep] == '*' or str(extra) == '{}':
            extra = ''

        hash = ''
        # Support for single hash (spec 1).
        if 'hash' in deps[dep]:
            hash = ' --hash={0}'.format(deps[dep]['hash'])

        # Support for multiple hashes (spec 2).
        if 'hashes' in deps[dep]:
            hash = '{0} '.format(''.join([' --hash={0} '.format(h) for h in deps[dep]['hashes']]))

        # Support for extras (e.g. requests[socks])
        if 'extras' in deps[dep]:
            extra = '[{0}]'.format(deps[dep]['extras'][0])

        if 'version' in deps[dep]:
            version = deps[dep]['version']

        # Support for version control
        maybe_vcs = [vcs for vcs in VCS_LIST if vcs in deps[dep]]
        vcs = maybe_vcs[0] if maybe_vcs else None

        # Support for files.
        if 'file' in deps[dep]:
            dep = deps[dep]['file']

        if vcs:
            extra = '{0}+{1}'.format(vcs, deps[dep][vcs])

            # Support for @refs.
            if 'ref' in deps[dep]:
                extra += '@{0}'.format(deps[dep]['ref'])

            extra += '#egg={0}'.format(dep)

            # Support for subdirectory
            if 'subdirectory' in deps[dep]:
                extra += '&subdirectory={0}'.format(deps[dep]['subdirectory'])

            # Support for editable.
            if 'editable' in deps[dep]:
                # Support for --egg.
                dep = '-e '
            else:
                dep = ''

        dependencies.append('{0}{1}{2}{3}'.format(dep, extra, version, hash))

    if not r:
        return dependencies

    # Write requirements.txt to tmp directory.
    f = tempfile.NamedTemporaryFile(suffix='-requirements.txt', delete=False)
    f.write('\n'.join(dependencies).encode('utf-8'))
    return f.name

def mkdir_p(newdir):
    """works the way a good mkdir should :)
        - already exists, silently complete
        - regular file in the way, raise an exception
        - parent directory(ies) does not exist, make them as well
        From: http://code.activestate.com/recipes/82465-a-friendly-mkdir/
    """

    if os.path.isdir(newdir):
        pass
    elif os.path.isfile(newdir):
        raise OSError("a file with the same name as the desired dir, '{0}', already exists.".format(newdir))
    else:
        head, tail = os.path.split(newdir)
        if head and not os.path.isdir(head):
            mkdir_p(head)
        if tail:
            os.mkdir(newdir)

def is_required_version(version, specified_version):
    """Check to see if there's a hard requirement for version
    number provided in the Pipfile.
    """

    # Certain packages may be defined with multiple values.
    if isinstance(specified_version, dict):
        specified_version = specified_version.get('version', '')
    if specified_version.startswith('=='):
        return version.strip() == specified_version.split('==')[1].strip()
    return True

def is_vcs(pipfile_entry):
    """Determine if dictionary entry from Pipfile is for a vcs dependency."""

    if isinstance(pipfile_entry, dict):
        return any(key for key in pipfile_entry.keys() if key in VCS_LIST)
    return False

def is_file(package):
    """Determine if a package name is for a File dependency."""
    for start in FILE_LIST:
        if package.startswith(start):
            return True

    return False

def pep440_version(version):
    """Normalize version to PEP 440 standards"""

    # Use pip built-in version parser.
    return str(pip.index.parse_version(version))

def pep423_name(name):
    """Normalize package name to PEP 423 style standard."""

    return name.lower().replace('_', '-')

def proper_case(package_name):
    """Properly case project name from pypi.org."""

    # Hit the simple API.
    r = requests.get('https://pypi.org/pypi/{0}/json'.format(package_name), timeout=0.3, stream=True)
    if not r.ok:
        raise IOError('Unable to find package {0} in PyPI repository.'.format(package_name))

    r = parse.parse('https://pypi.org/pypi/{name}/json', r.url)
    good_name = r['name']

    return good_name

def split_vcs(split_file):
    """Split VCS dependencies out from file."""

    if 'packages' in split_file or 'dev-packages' in split_file:
        sections = ('packages', 'dev-packages')
    elif 'default' in split_file or 'develop' in split_file:
        sections = ('default', 'develop')

    # For each vcs entry in a given section, move it to section-vcs.
    for section in sections:
        entries = split_file.get(section, {})
        vcs_dict = dict((k, entries.pop(k)) for k in list(entries.keys()) if is_vcs(entries[k]))
        split_file[section + '-vcs'] = vcs_dict

    return split_file

def recase_file(file_dict):
    """Recase file before writing to output."""

    if 'packages' in file_dict or 'dev-packages' in file_dict:
        sections = ('packages', 'dev-packages')
    elif 'default' in file_dict or 'develop' in file_dict:
        sections = ('default', 'develop')

    for section in sections:
        file_section = file_dict.get(section, {})

        # Try to properly case each key if we can.
        for key in list(file_section.keys()):
            try:
                cased_key = proper_case(key)
            except IOError:
                cased_key = key
            file_section[cased_key] = file_section.pop(key)

    return file_dict

def walk_up(bottom):
    """Mimic os.walk, but walk 'up' instead of down the directory tree.
    From: https://gist.github.com/zdavkeos/1098474
    """

    bottom = os.path.realpath(bottom)

    # Get files in current dir.
    try:
        names = os.listdir(bottom)
    except Exception:
        return

    dirs, nondirs = [], []
    for name in names:
        if os.path.isdir(os.path.join(bottom, name)):
            dirs.append(name)
        else:
            nondirs.append(name)

    yield bottom, dirs, nondirs

    new_path = os.path.realpath(os.path.join(bottom, '..'))

    # See if we are at the top.
    if new_path == bottom:
        return

    for x in walk_up(new_path):
        yield x

def find_requirements(max_depth=3):
    """Returns the path of a Pipfile in parent directories."""

    i = 0
    for c, d, f in walk_up(os.getcwd()):
        i += 1

        if i < max_depth:
            if 'requirements.txt':
                r = os.path.join(c, 'requirements.txt')
                if os.path.isfile(r):
                    return r
    raise RuntimeError('No requirements.txt found!')
<|endoftext|>


<commit_before>import logging
import asyncio
import argparse
import socket
from aiosmtpd.handlers import Message
from aiosmtpd.controller import Controller

import constants

from filters import Filter
from config import check_config

def bind(family, type, proto):
  """Create (or recreate) the actual socket object."""
  sock = socket.socket(family, type, proto)
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

  # If listening on IPv6, activate dual-stack.
  if family == socket.AF_INET6:
    sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)

  return sock

class EAController(Controller):
  def make_socket(self):
    host = self.hostname
    port = self.port
    try:
      # First try to determine the socket type.
        info = socket.getaddrinfo(
          host, port,
          socket.AF_UNSPEC,
          socket.SOCK_STREAM,
          0,
          socket.AI_PASSIVE,
        )
    except socket.gaierror:
      # Infer the type from the host.
        addr = host, port
        if ':' in host:
          addr += 0, 0
          type_ = socket.AF_INET6
        else:
          type_ = socket.AF_INET
          info_0 = type_, socket.SOCK_STREAM, 0, '', addr
          info = info_0,

    family, type, proto, canonname, addr = next(iter(info))
    sock = bind(family, type, proto)
    return sock

class MessageHandler(Message):
  filter_obj = None

  def __init__(self, message_class=None):
    super().__init__(message_class)
    self.filter_obj = Filter()

  def handle_message(self, message):
    logging.debug(message)
    self.filter_obj.filter(message['From'], message['To'], message['Subject'],
                           message.get_payload())

class EASMPTServer():

  host = 'localhost'
  port = 8025

  def __init__(self, host, port):
    self.host = host
    self.port = port
    logging.debug("Using host: %s and port %d for smtp server"
                  % (host, port))

  @asyncio.coroutine
  def serve(self, loop):
    controller = EAController(MessageHandler(), hostname=self.host,
                              port=self.port)
    controller.start()

  @asyncio.coroutine
  def stop(self):
    Controller.stop()

def main():
  parser = argparse.ArgumentParser(prog='email-actions')
  parser.add_argument('-v', '--version', action='version',
                      version='%(prog)s version ' + constants.VERSION)
  parser.add_argument('-H', '--hostname', action='store',
                      help='Host IP or name to bind the server to',
                      default='localhost')
  parser.add_argument('-p', '--port', type=int, action='store',
                      help='Port number to bind the server to',
                      default=8025)
  parser.add_argument('-l', '--log', type=int, action='store',
                      help='Set log level. 0=> Warning, 1=>Info, 2=>Debug',
                      default=0)
  req_args = parser.add_argument_group('required arguments')
  req_args.add_argument('-c', '--config', required=True,
                        help='Specify config file (yaml format) to be used. '
                        'If it doesn\'t exist, we\'ll try to create it')
  args = parser.parse_args()

  if args.log >= 2:
    log_level = logging.DEBUG
  elif args.log == 1:
    log_level = logging.INFO
  else:
    log_level = logging.WARNING
  logging.basicConfig(level=log_level,
                      format='%(asctime)s: [EA] %(filename)s '
                      '- %(message)s')

  cfg_status = check_config(args.config)

  if not cfg_status:
    exit(1)

  server = EASMPTServer(args.hostname, args.port)
  loop = asyncio.get_event_loop()
  loop.create_task(server.serve(loop))
  try:
    logging.info("Starting server")
    loop.run_forever()
  except KeyboardInterrupt:
    logging.info("Stopping server")
    server.stop()
    loop.stop()

if __name__ == "__main__":
  main()
<commit_msg>Run filters/actions asynchronously<commit_after>import logging
import asyncio
import argparse
import socket
from aiosmtpd.handlers import Message
from aiosmtpd.controller import Controller
from functools import partial
from concurrent.futures import ProcessPoolExecutor

import constants

from filters import Filter
from config import check_config

def bind(family, type, proto):
  """Create (or recreate) the actual socket object."""
  sock = socket.socket(family, type, proto)
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

  # If listening on IPv6, activate dual-stack.
  if family == socket.AF_INET6:
    sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)

  return sock

class EAController(Controller):
  def make_socket(self):
    host = self.hostname
    port = self.port
    try:
      # First try to determine the socket type.
        info = socket.getaddrinfo(
          host, port,
          socket.AF_UNSPEC,
          socket.SOCK_STREAM,
          0,
          socket.AI_PASSIVE,
        )
    except socket.gaierror:
      # Infer the type from the host.
        addr = host, port
        if ':' in host:
          addr += 0, 0
          type_ = socket.AF_INET6
        else:
          type_ = socket.AF_INET
          info_0 = type_, socket.SOCK_STREAM, 0, '', addr
          info = info_0,

    family, type, proto, canonname, addr = next(iter(info))
    sock = bind(family, type, proto)
    return sock

class MessageHandler(Message):
  filter_obj = None

  def __init__(self, message_class=None):
    super().__init__(message_class)
    self.filter_obj = Filter()

  def handle_message(self, message):
    logging.debug(message)
    loop = asyncio.get_event_loop()
    filter_action = partial(
      self.filter_obj.filter, message['From'], message['To'],
      message['Subject'], message.get_payload()
    )
    loop.run_in_executor(None, filter_action)

class EASMPTServer():

  host = 'localhost'
  port = 8025

  def __init__(self, host, port):
    self.host = host
    self.port = port
    logging.debug("Using host: %s and port %d for smtp server"
                  % (host, port))

  @asyncio.coroutine
  def serve(self, loop):
    controller = EAController(MessageHandler(), hostname=self.host,
                              port=self.port)
    controller.start()

  @asyncio.coroutine
  def stop(self):
    Controller.stop()

def main():
  parser = argparse.ArgumentParser(prog='email-actions')
  parser.add_argument('-v', '--version', action='version',
                      version='%(prog)s version ' + constants.VERSION)
  parser.add_argument('-H', '--hostname', action='store',
                      help='Host IP or name to bind the server to',
                      default='localhost')
  parser.add_argument('-p', '--port', type=int, action='store',
                      help='Port number to bind the server to',
                      default=8025)
  parser.add_argument('-l', '--log', type=int, action='store',
                      help='Set log level. 0=> Warning, 1=>Info, 2=>Debug',
                      default=0)
  req_args = parser.add_argument_group('required arguments')
  req_args.add_argument('-c', '--config', required=True,
                        help='Specify config file (yaml format) to be used. '
                        'If it doesn\'t exist, we\'ll try to create it')
  args = parser.parse_args()

  if args.log >= 2:
    log_level = logging.DEBUG
  elif args.log == 1:
    log_level = logging.INFO
  else:
    log_level = logging.WARNING
  logging.basicConfig(level=log_level,
                      format='%(asctime)s: [EA] %(filename)s '
                      '- %(message)s')

  cfg_status = check_config(args.config)

  if not cfg_status:
    exit(1)

  server = EASMPTServer(args.hostname, args.port)
  loop = asyncio.get_event_loop()
  loop.create_task(server.serve(loop))
  try:
    logging.info("Starting server")
    loop.run_forever()
  except KeyboardInterrupt:
    logging.info("Stopping server")
    server.stop()
    loop.stop()

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


<commit_before># -*- coding: utf-8 -*-
from django.conf import settings
from django.db.models import Manager, ForeignKey
from django.db.models.base import ModelBase

from modeltranslation import settings as mt_settings
from modeltranslation.fields import (TranslationFieldDescriptor, TranslatedRelationIdDescriptor,
                                     create_translation_field)
from modeltranslation.manager import MultilingualManager, rewrite_lookup_key
from modeltranslation.utils import build_localized_fieldname

class AlreadyRegistered(Exception):
    pass

class NotRegistered(Exception):
    pass

class DescendantRegistered(Exception):
    pass

class FieldsAggregationMetaClass(type):
    """
    Metaclass to handle custom inheritance of fields between classes.
    """
    def __new__(cls, name, bases, attrs):
        attrs['fields'] = set(attrs.get('fields', ()))
        for base in bases:
            if isinstance(base, FieldsAggregationMetaClass):
                attrs['fields'].update(base.fields)
        attrs['fields'] = tuple(attrs['fields'])
        return super(FieldsAggregationMetaClass, cls).__new__(cls, name, bases, attrs)

class TranslationOptions(object):
    """
    Translatable fields are declared by registering a model using
    ``TranslationOptions`` class with appropriate ``fields`` attribute.
    Model-specific fallback values and languages can also be given as class
    attributes.

    Options instances hold info about translatable fields for a model and its
    superclasses. The ``local_fields`` and ``fields`` attributes are mappings
    from fields to sets of their translation fields; ``local_fields`` contains
    only those fields that are handled in the model's database table (those
    inherited from abstract superclasses, unless there is a concrete superclass
    in between in the inheritance chain), while ``fields`` also includes fields
    inherited from concrete supermodels (giving all translated fields available
    on a model).

    ``related`` attribute inform whether this model is related part of some relation
    with translated model. This model may be not translated itself.
    ``related_fields`` contains names of reverse lookup fields.
    """
    __metaclass__ = FieldsAggregationMetaClass

    def __init__(self, model):
        """
        Create fields dicts without any translation fields.
        """
        self.model = model
        self.registered = False
        self.related = False
        self.local_fields = dict((f, set()) for f in self.fields)
        self.fields = dict((f, set()) for f in self.fields)
        self.related_fields = []

    def update(self, other):
        """
        Update with options from a superclass.
        """
        if other.model._meta.abstract:
            self.local_fields.update(other.local_fields)
        self.fields.update(other.fields)

    def add_translation_field(self, field, translation_field):
        """
        Add a new translation field to both fields dicts.
        """
        self.local_fields[field].add(translation_field)
        self.fields[field].add(translation_field)

    def get_field_names(self):
        """
        Return name of all fields that can be used in filtering.
        """
        return self.fields.keys() + self.related_fields

    def __str__(self):
        local = tuple(self.local_fields.keys())
        inherited = tuple(set(self.fields.keys()) - set(local))
        return '%s: %s + %s' % (self.__class__.__name__, local, inherited)

def add_translation_fields(model, opts):
    """
    Monkey patches the original model class to provide additional fields for
    every language.

    Adds newly created translation fields to the given translation options.
    """
    for field_name in opts.local_fields.iterkeys():
        for l in settings.LANGUAGES:
            # Create a dynamic translation field
            translation_field = create_translation_field(
                model=model, field_name=field_name, lang=l[0])
            # Construct the name for the localized field
            localized_field_name = build_localized_fieldname(field_name, l[0])
            # Check if the model already has a field by that name
            if hasattr(model, localized_field_name):
                raise ValueError(
                    "Error adding translation field. Model '%s' already contains a field named"
                    "'%s'." % (model._meta.object_name, localized_field_name))
            # This approach implements the translation fields as full valid
            # django model fields and therefore adds them via add_to_class
            model.add_to_class(localized_field_name, translation_field)
            opts.add_translation_field(field_name, translation_field)

def add_manager(model):
    """
    Monkey patches the original model to use MultilingualManager instead of
    default manager (``objects``).

    If model has a custom manager, then merge it with MultilingualManager.
    """
    if not hasattr(model, 'objects'):
        return
    current_manager = model.objects
    if isinstance(current_manager, MultilingualManager):
        return
    if current_manager.__class__ is Manager:
        current_manager.__class__ = MultilingualManager
    else:
        class NewMultilingualManager(MultilingualManager, current_manager.__class__):
            pass
        current_manager.__class__ = NewMultilingualManager

def patch_constructor(model):
    """
    Monkey patches the original model to rewrite fields names in __init__
    """
    old_init = model.__init__

    def new_init(self, *args, **kwargs):
        self._mt_init = True
        if not self._deferred:
            populate_translation_fields(self.__class__, kwargs)
            for key, val in kwargs.items():
                new_key = rewrite_lookup_key(model, key)
                # Old key is intentionally left in case old_init wants to play with it
                kwargs.setdefault(new_key, val)
        old_init(self, *args, **kwargs)
        del self._mt_init
    model.__init__ = new_init

def patch_metaclass(model):
    """
    Monkey patches original model metaclass to exclude translated fields on deferred subclasses.
    """
    old_mcs = model.__class__

    class translation_deferred_mcs(old_mcs):
        """
        This metaclass is essential for deferred subclasses (obtained via only/defer) to work.

        When deferred subclass is created, some translated fields descriptors could be overridden
        by DeferredAttribute - which would cause translation retrieval to fail.
        Prevent this from happening with deleting those attributes from class being created.
        This metaclass would be called from django.db.models.query_utils.deferred_class_factory
        """
        def __new__(cls, name, bases, attrs):
            if attrs.get('_deferred', False):
                opts = translator.get_options_for_model(model)
                for field_name in opts.fields.iterkeys():
                    attrs.pop(field_name, None)
            return super(translation_deferred_mcs, cls).__new__(cls, name, bases, attrs)
    # Assign to __metaclass__ wouldn't work, since metaclass search algorithm check for __class__.
    # http://docs.python.org/2/reference/datamodel.html#__metaclass__
    model.__class__ = translation_deferred_mcs

def delete_cache_fields(model):
    opts = model._meta
    try:
        del opts._field_cache
    except AttributeError:
        pass
    try:
        del opts._field_name_cache
    except AttributeError:
        pass
    try:
        del opts._name_map
    except AttributeError:
        pass

def populate_translation_fields(sender, kwargs):
    """
    When models are created or loaded from fixtures, replicates values
    provided for translatable fields to some / all empty translation fields,
    according to the current population mode.

    Population is performed only on keys (field names) present in kwargs.
    Nothing is returned, but passed kwargs dictionary is altered.

    With ``mode`` set to:
    -- ``all``: fills all translation fields, skipping just those for
       which a translated value is also provided;
    -- ``default``: fills only the default translation (unless it is
       additionally provided);
    -- ``required``: like ``default``, but only if the original field is
       non-nullable;

    At least the ``required`` mode should be used when loading untranslated
    fixtures to keep the database consistent (note that Django management
    commands are normally forced to run with hardcoded ``en-us`` language
    active). The ``default`` mode is useful if you need to ensure fallback
    values are available, and ``all`` if you need to have all translations
    defined (for example to make lookups / filtering without resorting to
    query fallbacks).
    """
    populate = mt_settings.AUTO_POPULATE
    if not populate:
        return
    if populate is True:
        # What was meant by ``True`` is now called ``all``.
        populate = 'all'

    opts = translator.get_options_for_model(sender)
    for key, val in kwargs.items():
        if key in opts.fields:
            if populate == 'all':
                # Set the value for every language.
                for translation_field in opts.fields[key]:
                    kwargs.setdefault(translation_field.name, val)
            elif populate == 'default':
                default = build_localized_fieldname(key, mt_settings.DEFAULT_LANGUAGE)
                kwargs.setdefault(default, val)
            elif populate == 'required':
                default = build_localized_fieldname(key, mt_settings.DEFAULT_LANGUAGE)
                if not sender._meta.get_field(key).null:
                    kwargs.setdefault(default, val)
            else:
                raise AttributeError("Unknown population mode '%s'." % populate)

class Translator(object):
    """
    A Translator object encapsulates an instance of a translator. Models are
    registered with the Translator using the register() method.
    """
    def __init__(self):
        # All seen models (model class -> ``TranslationOptions`` instance).
        self._registry = {}

    def register(self, model_or_iterable, opts_class=None, **options):
        """
        Registers the given model(s) with the given translation options.

        The model(s) should be Model classes, not instances.

        Fields declared for translation on a base class are inherited by
        subclasses. If the model or one of its subclasses is already
        registered for translation, this will raise an exception.
        """
        if isinstance(model_or_iterable, ModelBase):
            model_or_iterable = [model_or_iterable]

        for model in model_or_iterable:
            # Ensure that a base is not registered after a subclass (_registry
            # is closed with respect to taking bases, so we can just check if
            # we've seen the model).
            if model in self._registry:
                if self._registry[model].registered:
                    raise AlreadyRegistered(
                        'Model "%s" is already registered for translation' %
                        model.__name__)
                else:
                    descendants = [d.__name__ for d in self._registry.keys()
                                   if issubclass(d, model) and d != model]
                    raise DescendantRegistered(
                        'Model "%s" cannot be registered after its subclass'
                        ' "%s"' % (model.__name__, descendants[0]))

            # Find inherited fields and create options instance for the model.
            opts = self._get_options_for_model(model, opts_class, **options)

            # Mark the object explicitly as registered -- registry caches
            # options of all models, registered or not.
            opts.registered = True

            # Add translation fields to the model.
            add_translation_fields(model, opts)

            # Delete all fields cache for related model (parent and children)
            for related_obj in model._meta.get_all_related_objects():
                delete_cache_fields(related_obj.model)

            # Set MultilingualManager
            add_manager(model)

            # Patch __init__ to rewrite fields
            patch_constructor(model)

            # Patch __metaclass__ to allow deferring to work
            patch_metaclass(model)

            # Substitute original field with descriptor
            model_fallback_values = getattr(opts, 'fallback_values', None)
            model_fallback_languages = getattr(opts, 'fallback_languages', None)
            for field_name in opts.local_fields.iterkeys():
                if model_fallback_values is None:
                    field_fallback_value = None
                elif isinstance(model_fallback_values, dict):
                    field_fallback_value = model_fallback_values.get(field_name, None)
                else:
                    field_fallback_value = model_fallback_values
                field = model._meta.get_field(field_name)
                descriptor = TranslationFieldDescriptor(
                    field,
                    fallback_value=field_fallback_value,
                    fallback_languages=model_fallback_languages)
                setattr(model, field_name, descriptor)
                if isinstance(field, ForeignKey):
                    # We need to use a special descriptor so that
                    # _id fields on translated ForeignKeys work
                    # as expected.
                    desc = TranslatedRelationIdDescriptor(field_name, model_fallback_languages)
                    setattr(model, field.get_attname(), desc)

                    # Set related field names on other model
                    if not field.rel.is_hidden():
                        other_opts = self._get_options_for_model(field.rel.to)
                        other_opts.related = True
                        other_opts.related_fields.append(field.related_query_name())
                        add_manager(field.rel.to)  # Add manager in case of non-registered model

    def unregister(self, model_or_iterable):
        """
        Unregisters the given model(s).

        If a model isn't registered, this will raise NotRegistered. If one of
        its subclasses is registered, DescendantRegistered will be raised.
        """
        if isinstance(model_or_iterable, ModelBase):
            model_or_iterable = [model_or_iterable]
        for model in model_or_iterable:
            # Check if the model is actually registered (``get_options_for_model``
            # throws an exception if it's not).
            self.get_options_for_model(model)
            # Invalidate all submodels options and forget about
            # the model itself.
            for desc, desc_opts in self._registry.items():
                if not issubclass(desc, model):
                    continue
                if model != desc and desc_opts.registered:
                    # Allowing to unregister a base would necessitate
                    # repatching all submodels.
                    raise DescendantRegistered(
                        'You need to unregister descendant "%s" before'
                        ' unregistering its base "%s"' %
                        (desc.__name__, model.__name__))
                del self._registry[desc]

    def get_registered_models(self, abstract=True):
        """
        Returns a list of all registered models, or just concrete
        registered models.
        """
        return [model for (model, opts) in self._registry.items()
                if opts.registered and (not model._meta.abstract or abstract)]

    def _get_options_for_model(self, model, opts_class=None, **options):
        """
        Returns an instance of translation options with translated fields
        defined for the ``model`` and inherited from superclasses.
        """
        if model not in self._registry:
            # Create a new type for backwards compatibility.
            opts = type("%sTranslationOptions" % model.__name__,
                        (opts_class or TranslationOptions,), options)(model)

            # Fields for translation may be inherited from abstract
            # superclasses, so we need to look at all parents.
            for base in model.__bases__:
                if not hasattr(base, '_meta'):
                    # Things without _meta aren't functional models, so they're
                    # uninteresting parents.
                    continue
                opts.update(self._get_options_for_model(base))

            # Cache options for all models -- we may want to compute options
            # of registered subclasses of unregistered models.
            self._registry[model] = opts

        return self._registry[model]

    def get_options_for_model(self, model):
        """
        Thin wrapper around ``_get_options_for_model`` to preserve the
        semantic of throwing exception for models not directly registered.
        """
        opts = self._get_options_for_model(model)
        if not opts.registered and not opts.related:
            raise NotRegistered('The model "%s" is not registered for '
                                'translation' % model.__name__)
        return opts

# This global object represents the singleton translator object
translator = Translator()
<commit_msg>Fix test running in Django 1.6: model opts function caching.<commit_after># -*- coding: utf-8 -*-
from django.conf import settings
from django.db.models import Manager, ForeignKey
from django.db.models.base import ModelBase

from modeltranslation import settings as mt_settings
from modeltranslation.fields import (TranslationFieldDescriptor, TranslatedRelationIdDescriptor,
                                     create_translation_field)
from modeltranslation.manager import MultilingualManager, rewrite_lookup_key
from modeltranslation.utils import build_localized_fieldname

class AlreadyRegistered(Exception):
    pass

class NotRegistered(Exception):
    pass

class DescendantRegistered(Exception):
    pass

class FieldsAggregationMetaClass(type):
    """
    Metaclass to handle custom inheritance of fields between classes.
    """
    def __new__(cls, name, bases, attrs):
        attrs['fields'] = set(attrs.get('fields', ()))
        for base in bases:
            if isinstance(base, FieldsAggregationMetaClass):
                attrs['fields'].update(base.fields)
        attrs['fields'] = tuple(attrs['fields'])
        return super(FieldsAggregationMetaClass, cls).__new__(cls, name, bases, attrs)

class TranslationOptions(object):
    """
    Translatable fields are declared by registering a model using
    ``TranslationOptions`` class with appropriate ``fields`` attribute.
    Model-specific fallback values and languages can also be given as class
    attributes.

    Options instances hold info about translatable fields for a model and its
    superclasses. The ``local_fields`` and ``fields`` attributes are mappings
    from fields to sets of their translation fields; ``local_fields`` contains
    only those fields that are handled in the model's database table (those
    inherited from abstract superclasses, unless there is a concrete superclass
    in between in the inheritance chain), while ``fields`` also includes fields
    inherited from concrete supermodels (giving all translated fields available
    on a model).

    ``related`` attribute inform whether this model is related part of some relation
    with translated model. This model may be not translated itself.
    ``related_fields`` contains names of reverse lookup fields.
    """
    __metaclass__ = FieldsAggregationMetaClass

    def __init__(self, model):
        """
        Create fields dicts without any translation fields.
        """
        self.model = model
        self.registered = False
        self.related = False
        self.local_fields = dict((f, set()) for f in self.fields)
        self.fields = dict((f, set()) for f in self.fields)
        self.related_fields = []

    def update(self, other):
        """
        Update with options from a superclass.
        """
        if other.model._meta.abstract:
            self.local_fields.update(other.local_fields)
        self.fields.update(other.fields)

    def add_translation_field(self, field, translation_field):
        """
        Add a new translation field to both fields dicts.
        """
        self.local_fields[field].add(translation_field)
        self.fields[field].add(translation_field)

    def get_field_names(self):
        """
        Return name of all fields that can be used in filtering.
        """
        return self.fields.keys() + self.related_fields

    def __str__(self):
        local = tuple(self.local_fields.keys())
        inherited = tuple(set(self.fields.keys()) - set(local))
        return '%s: %s + %s' % (self.__class__.__name__, local, inherited)

def add_translation_fields(model, opts):
    """
    Monkey patches the original model class to provide additional fields for
    every language.

    Adds newly created translation fields to the given translation options.
    """
    for field_name in opts.local_fields.iterkeys():
        for l in settings.LANGUAGES:
            # Create a dynamic translation field
            translation_field = create_translation_field(
                model=model, field_name=field_name, lang=l[0])
            # Construct the name for the localized field
            localized_field_name = build_localized_fieldname(field_name, l[0])
            # Check if the model already has a field by that name
            if hasattr(model, localized_field_name):
                raise ValueError(
                    "Error adding translation field. Model '%s' already contains a field named"
                    "'%s'." % (model._meta.object_name, localized_field_name))
            # This approach implements the translation fields as full valid
            # django model fields and therefore adds them via add_to_class
            model.add_to_class(localized_field_name, translation_field)
            opts.add_translation_field(field_name, translation_field)

def add_manager(model):
    """
    Monkey patches the original model to use MultilingualManager instead of
    default manager (``objects``).

    If model has a custom manager, then merge it with MultilingualManager.
    """
    if not hasattr(model, 'objects'):
        return
    current_manager = model.objects
    if isinstance(current_manager, MultilingualManager):
        return
    if current_manager.__class__ is Manager:
        current_manager.__class__ = MultilingualManager
    else:
        class NewMultilingualManager(MultilingualManager, current_manager.__class__):
            pass
        current_manager.__class__ = NewMultilingualManager

def patch_constructor(model):
    """
    Monkey patches the original model to rewrite fields names in __init__
    """
    old_init = model.__init__

    def new_init(self, *args, **kwargs):
        self._mt_init = True
        if not self._deferred:
            populate_translation_fields(self.__class__, kwargs)
            for key, val in kwargs.items():
                new_key = rewrite_lookup_key(model, key)
                # Old key is intentionally left in case old_init wants to play with it
                kwargs.setdefault(new_key, val)
        old_init(self, *args, **kwargs)
        del self._mt_init
    model.__init__ = new_init

def patch_metaclass(model):
    """
    Monkey patches original model metaclass to exclude translated fields on deferred subclasses.
    """
    old_mcs = model.__class__

    class translation_deferred_mcs(old_mcs):
        """
        This metaclass is essential for deferred subclasses (obtained via only/defer) to work.

        When deferred subclass is created, some translated fields descriptors could be overridden
        by DeferredAttribute - which would cause translation retrieval to fail.
        Prevent this from happening with deleting those attributes from class being created.
        This metaclass would be called from django.db.models.query_utils.deferred_class_factory
        """
        def __new__(cls, name, bases, attrs):
            if attrs.get('_deferred', False):
                opts = translator.get_options_for_model(model)
                for field_name in opts.fields.iterkeys():
                    attrs.pop(field_name, None)
            return super(translation_deferred_mcs, cls).__new__(cls, name, bases, attrs)
    # Assign to __metaclass__ wouldn't work, since metaclass search algorithm check for __class__.
    # http://docs.python.org/2/reference/datamodel.html#__metaclass__
    model.__class__ = translation_deferred_mcs

def delete_cache_fields(model):
    opts = model._meta
    cached_attrs = ('_field_cache', '_field_name_cache', '_name_map', 'fields', 'concrete_fields',
                    'local_concrete_fields')
    for attr in cached_attrs:
        try:
            delattr(opts, attr)
        except AttributeError:
            pass

def populate_translation_fields(sender, kwargs):
    """
    When models are created or loaded from fixtures, replicates values
    provided for translatable fields to some / all empty translation fields,
    according to the current population mode.

    Population is performed only on keys (field names) present in kwargs.
    Nothing is returned, but passed kwargs dictionary is altered.

    With ``mode`` set to:
    -- ``all``: fills all translation fields, skipping just those for
       which a translated value is also provided;
    -- ``default``: fills only the default translation (unless it is
       additionally provided);
    -- ``required``: like ``default``, but only if the original field is
       non-nullable;

    At least the ``required`` mode should be used when loading untranslated
    fixtures to keep the database consistent (note that Django management
    commands are normally forced to run with hardcoded ``en-us`` language
    active). The ``default`` mode is useful if you need to ensure fallback
    values are available, and ``all`` if you need to have all translations
    defined (for example to make lookups / filtering without resorting to
    query fallbacks).
    """
    populate = mt_settings.AUTO_POPULATE
    if not populate:
        return
    if populate is True:
        # What was meant by ``True`` is now called ``all``.
        populate = 'all'

    opts = translator.get_options_for_model(sender)
    for key, val in kwargs.items():
        if key in opts.fields:
            if populate == 'all':
                # Set the value for every language.
                for translation_field in opts.fields[key]:
                    kwargs.setdefault(translation_field.name, val)
            elif populate == 'default':
                default = build_localized_fieldname(key, mt_settings.DEFAULT_LANGUAGE)
                kwargs.setdefault(default, val)
            elif populate == 'required':
                default = build_localized_fieldname(key, mt_settings.DEFAULT_LANGUAGE)
                if not sender._meta.get_field(key).null:
                    kwargs.setdefault(default, val)
            else:
                raise AttributeError("Unknown population mode '%s'." % populate)

class Translator(object):
    """
    A Translator object encapsulates an instance of a translator. Models are
    registered with the Translator using the register() method.
    """
    def __init__(self):
        # All seen models (model class -> ``TranslationOptions`` instance).
        self._registry = {}

    def register(self, model_or_iterable, opts_class=None, **options):
        """
        Registers the given model(s) with the given translation options.

        The model(s) should be Model classes, not instances.

        Fields declared for translation on a base class are inherited by
        subclasses. If the model or one of its subclasses is already
        registered for translation, this will raise an exception.
        """
        if isinstance(model_or_iterable, ModelBase):
            model_or_iterable = [model_or_iterable]

        for model in model_or_iterable:
            # Ensure that a base is not registered after a subclass (_registry
            # is closed with respect to taking bases, so we can just check if
            # we've seen the model).
            if model in self._registry:
                if self._registry[model].registered:
                    raise AlreadyRegistered(
                        'Model "%s" is already registered for translation' %
                        model.__name__)
                else:
                    descendants = [d.__name__ for d in self._registry.keys()
                                   if issubclass(d, model) and d != model]
                    raise DescendantRegistered(
                        'Model "%s" cannot be registered after its subclass'
                        ' "%s"' % (model.__name__, descendants[0]))

            # Find inherited fields and create options instance for the model.
            opts = self._get_options_for_model(model, opts_class, **options)

            # Mark the object explicitly as registered -- registry caches
            # options of all models, registered or not.
            opts.registered = True

            # Add translation fields to the model.
            add_translation_fields(model, opts)

            # Delete all fields cache for related model (parent and children)
            for related_obj in model._meta.get_all_related_objects():
                delete_cache_fields(related_obj.model)

            # Set MultilingualManager
            add_manager(model)

            # Patch __init__ to rewrite fields
            patch_constructor(model)

            # Patch __metaclass__ to allow deferring to work
            patch_metaclass(model)

            # Substitute original field with descriptor
            model_fallback_values = getattr(opts, 'fallback_values', None)
            model_fallback_languages = getattr(opts, 'fallback_languages', None)
            for field_name in opts.local_fields.iterkeys():
                if model_fallback_values is None:
                    field_fallback_value = None
                elif isinstance(model_fallback_values, dict):
                    field_fallback_value = model_fallback_values.get(field_name, None)
                else:
                    field_fallback_value = model_fallback_values
                field = model._meta.get_field(field_name)
                descriptor = TranslationFieldDescriptor(
                    field,
                    fallback_value=field_fallback_value,
                    fallback_languages=model_fallback_languages)
                setattr(model, field_name, descriptor)
                if isinstance(field, ForeignKey):
                    # We need to use a special descriptor so that
                    # _id fields on translated ForeignKeys work
                    # as expected.
                    desc = TranslatedRelationIdDescriptor(field_name, model_fallback_languages)
                    setattr(model, field.get_attname(), desc)

                    # Set related field names on other model
                    if not field.rel.is_hidden():
                        other_opts = self._get_options_for_model(field.rel.to)
                        other_opts.related = True
                        other_opts.related_fields.append(field.related_query_name())
                        add_manager(field.rel.to)  # Add manager in case of non-registered model

    def unregister(self, model_or_iterable):
        """
        Unregisters the given model(s).

        If a model isn't registered, this will raise NotRegistered. If one of
        its subclasses is registered, DescendantRegistered will be raised.
        """
        if isinstance(model_or_iterable, ModelBase):
            model_or_iterable = [model_or_iterable]
        for model in model_or_iterable:
            # Check if the model is actually registered (``get_options_for_model``
            # throws an exception if it's not).
            self.get_options_for_model(model)
            # Invalidate all submodels options and forget about
            # the model itself.
            for desc, desc_opts in self._registry.items():
                if not issubclass(desc, model):
                    continue
                if model != desc and desc_opts.registered:
                    # Allowing to unregister a base would necessitate
                    # repatching all submodels.
                    raise DescendantRegistered(
                        'You need to unregister descendant "%s" before'
                        ' unregistering its base "%s"' %
                        (desc.__name__, model.__name__))
                del self._registry[desc]

    def get_registered_models(self, abstract=True):
        """
        Returns a list of all registered models, or just concrete
        registered models.
        """
        return [model for (model, opts) in self._registry.items()
                if opts.registered and (not model._meta.abstract or abstract)]

    def _get_options_for_model(self, model, opts_class=None, **options):
        """
        Returns an instance of translation options with translated fields
        defined for the ``model`` and inherited from superclasses.
        """
        if model not in self._registry:
            # Create a new type for backwards compatibility.
            opts = type("%sTranslationOptions" % model.__name__,
                        (opts_class or TranslationOptions,), options)(model)

            # Fields for translation may be inherited from abstract
            # superclasses, so we need to look at all parents.
            for base in model.__bases__:
                if not hasattr(base, '_meta'):
                    # Things without _meta aren't functional models, so they're
                    # uninteresting parents.
                    continue
                opts.update(self._get_options_for_model(base))

            # Cache options for all models -- we may want to compute options
            # of registered subclasses of unregistered models.
            self._registry[model] = opts

        return self._registry[model]

    def get_options_for_model(self, model):
        """
        Thin wrapper around ``_get_options_for_model`` to preserve the
        semantic of throwing exception for models not directly registered.
        """
        opts = self._get_options_for_model(model)
        if not opts.registered and not opts.related:
            raise NotRegistered('The model "%s" is not registered for '
                                'translation' % model.__name__)
        return opts

# This global object represents the singleton translator object
translator = Translator()
<|endoftext|>


<commit_before>import gtk
import cairo

# -- Base classes for painting sound visualization.
#
# Defined step by step only for code clarity. Everything could as well
# be stuck in one class.

class CairoWidget(gtk.DrawingArea):

    __gsignals__ = {"expose-event": "override"}

    def __init__(self):
        super(CairoWidget, self).__init__()

    def do_expose_event(self, event):
        context = self.window.cairo_create()
        context.rectangle(event.area.x, event.area.y,
                          event.area.width, event.area.height)
        context.clip()
        width, height = self.window.get_size()
        self.draw(context, width, height)

    def update(self):
        # queue_draw() emits an expose event. Double buffering is used
        # automatically in the expose event handler.
        self.queue_draw()

    def draw(self, context, width, height):
        """Must be overriden to draw to the cairo context."""
        pass

class LayeredCairoWidget(CairoWidget):
    """A widget with several layers.

    This widget paints itself by successively passing its context to
    layer objects. The draw() method of a layer object must paint to
    the context.

    """
    def __init__(self):
        super(LayeredCairoWidget, self).__init__()
        self.layers = []

    def draw(self, context, width, height):
        for layer in self.layers:
            layer.draw(context, width, height)

class LayeredGraphView(LayeredCairoWidget):
    """A layered widget dedicated to paint data from a Graph
    object.

    As soon as it knows its width, it gives it to the Graph
    object.

    """
    def __init__(self, graph):
        super(LayeredGraphView, self).__init__()
        self._graph = graph
        self.connect("size_allocate", self.resize)

    def resize(self, widget, rect):
        self._graph.set_width(rect.width)

# -- Aah, beautiful sound visualization widget.

class GraphView(LayeredGraphView):
    """Sound visualization widget for the main window.

    * Two graphical layers: the waveform and the selection.
    * Mouse event listeners act on models (scroll and selection).

    """
    def __init__(self, graph, selection):
        super(GraphView, self).__init__(graph)
        self._graph = graph
        self._selection = selection
        self.layers.append(WaveformLayer(self, graph))
        self.layers.append(SelectionLayer(self, selection))
        MouseSelection(self, selection)
        MouseScroll(self, graph)

# -- The layers that can be added to LayeredGraphview.

class WaveformLayer(object):
    """A layer for LayeredGraphView.

    It paints the graph (the waveform). The cairo surface is cached,
    it is redrawn only when graph has changed.

    """
    def __init__(self, layered, graph):
        self._layered = layered
        self._graph = graph
        self._cache = None
        layered.add_events(gtk.gdk.SCROLL_MASK)
        graph.changed.connect(self.update)

    def update(self):
        self._cache = None
        self._layered.update()

    def draw(self, context, width, height):
        if not self._cache:
            surface = context.get_target()
            self._cache = surface.create_similar(cairo.CONTENT_COLOR,
                                                 width, height)
            c = cairo.Context(self._cache)

            # black background
            c.set_source_rgb(0, 0, 0)
            c.paint()

            # line at zero
            c.set_line_width(1)
            c.set_source_rgb(0.2, 0.2, 0.2)
            c.move_to(0, round(height / 2) + 0.5)
            c.line_to(width, round(height / 2) + 0.5)
            c.stroke()

            # waveform
            c.set_source_rgb(0, 0.9, 0)
            overview = self._graph.get_values()
            for i, value in enumerate(overview):
                x = i
                y = round((-value * 0.5 + 0.5) * height)
                #c.rectangle(x, y, 1, 1)
                #c.fill()
                c.move_to(x + 0.5, 0.5 * height + 0.5)
                c.line_to(x + 0.5, y + 0.5)
                c.stroke()

        context.set_source_surface(self._cache, 0, 0)
        context.set_operator(cairo.OPERATOR_SOURCE)
        context.paint()

class SelectionLayer(object):
    """A layer for LayeredGraphView.

    It highlights the selected area. It also listens to mouse events
    and changes the selection accordingly. The cairo surface is
    cached, it is redrawn only when selection has changed.

    """
    def __init__(self, layered, selection):
        self._layered = layered
        self._cache = None
        self._selection = selection
        self._selection.changed.connect(self.update)
        layered.add_events(gtk.gdk.BUTTON_PRESS_MASK |
                           gtk.gdk.BUTTON_RELEASE_MASK |
                           gtk.gdk.POINTER_MOTION_MASK |
                           gtk.gdk.POINTER_MOTION_HINT_MASK)

    def update(self):
        self._cache = None
        self._layered.update()

    def draw(self, context, width, height):
        if not self._cache:
            surface = context.get_target()
            self._cache = surface.create_similar(cairo.CONTENT_COLOR_ALPHA,
                                                 width, height)
            c = cairo.Context(self._cache)
            start, end = self._selection.pixels()

            if start != end:
                # darken everything ...
                c.set_source_rgba(0, 0, 0, 0.75)
                c.paint()

                # ... then clear selection
                c.set_operator(cairo.OPERATOR_CLEAR)
                c.rectangle(start + 0.5, 0, end - start + 0.5, height)
                c.fill()

            # cursors
            c.set_operator(cairo.OPERATOR_ADD)
            c.set_line_width(1)
            c.set_source_rgba(1, 1, 1, 0.7)
            c.move_to(start + 0.5, 0)
            c.line_to(start + 0.5, height)
            c.move_to(end + 0.5, 0)
            c.line_to(end + 0.5, height)
            c.stroke()

        context.set_source_surface(self._cache, 0, 0)
        context.set_operator(cairo.OPERATOR_OVER)
        context.paint()

# -- Mouse event listeners that act on models.

class MouseScroll(object):
    """Listens for mouse wheel events and scroll a graph

    Must be attached to a gtk.Widget and a Graph.

    """
    def __init__(self, widget, graph):
        self._graph = graph
        widget.connect("scroll_event", self.scroll_event)

    def scroll_event(self, widget, event):
        if event.direction in (gtk.gdk.SCROLL_UP, gtk.gdk.SCROLL_LEFT):
            self._graph.scroll_left()
        elif event.direction in (gtk.gdk.SCROLL_DOWN, gtk.gdk.SCROLL_RIGHT):
            self._graph.scroll_right()

class MouseSelection(object):
    """Listens for mouse events and select graph area

    Must be attached to a gtk.Widget and a Graph.

    """
    def __init__(self, widget, selection):
        self._selection = selection
        self.pressed = False
        widget.connect("button_press_event", self.button_press)
        widget.connect("button_release_event", self.button_release)
        widget.connect("motion_notify_event", self.motion_notify)

    def button_press(self, widget, event):
        if event.button == 1:
            self.pressed = True
            self._selection.start_selection(event.x)

    def motion_notify(self, widget, event):
        if self.pressed:
            x = event.window.get_pointer()[0]
            self._selection.end_selection(x)

    def button_release(self, widget, event):
        if event.button == 1:
            self.pressed = False

# -- An horizontal scrollbar, derived to control a graph model.

class GraphScrollbar(gtk.HScrollbar):
    """An horizontal scrollbar that acts on a Graph.

    Acts on a graph object and is updated when the graph
    object changes.

    """
    def __init__(self, graph):
        self._adjustment = gtk.Adjustment(0, 0, 1, 0.1, 0, 1)
        super(GraphScrollbar, self).__init__(self._adjustment)
        self._graph = graph
        self._graph.changed.connect(self.update_scrollbar)
        self.connect("value-changed", self.update_model)

        # When the scrollbar or the graph model changes, this
        # attribute must be set to True to avoid infinite feedback
        # between them. Example of what happens otherwise: scrollbar
        # changes --> graph changes --> scrollbar changes --> ...
        self.inhibit = False

    def update_model(self, widget):
        """Changes the model.

        Called when the scrollbar has been moved by the user.

        """
        if not self.inhibit:
            self.inhibit = True
            self._graph.view_starts_at(self._adjustment.value)
            self.inhibit = False

    def update_scrollbar(self):
        """Changes the scrollbar.

        Called when the model has changed.

        """
        if not self.inhibit:
            self.inhibit = True
            length, start, end = self._graph.get_info()
            self._adjustment.upper = length
            self._adjustment.value = start
            self._adjustment.page_size = (end - start)
            self.inhibit = False

# -- Testing

if __name__ == '__main__':
    from mock import Mock, Fake

    def test_window():
        window = gtk.Window()
        window.resize(500, 200)
        window.connect("delete-event", gtk.main_quit)
        graph = Mock({"get_values": [v / 500. for v in xrange(500)],
                     "set_width": None,
                     "get_info": (0, 0, 0)})
        graph.changed = Fake()
        layered = LayeredGraphView(graph)
        layered.layers.append(WaveformLayer(layered, graph))
        window.add(layered)
        window.show_all()
        gtk.main()

    def test_rand():
        window = gtk.Window()
        window.resize(500, 200)
        window.connect("delete-event", gtk.main_quit)

        from random import random
        values = [(random() - 0.5) * 2 for i in xrange(500)]
        graph = Mock({"get_values": values, "set_width": None,
                          "get_info": (0, 0, 0)})
        graph.changed = Fake()
        layered = LayeredGraphView(graph)
        layered.layers.append(WaveformLayer(layered, graph))
        window.add(layered)
        window.show_all()
        gtk.main()

    def test_sine():
        window = gtk.Window()
        window.resize(500, 200)
        window.connect("delete-event", gtk.main_quit)

        from math import sin
        sine = [sin(2 * 3.14 * 0.01 * x) for x in xrange(500)]
        graph = Mock({"get_values": sine, "set_width": None,
                          "get_info": (0, 0, 0)})
        graph.changed = Fake()
        layered = LayeredGraphView(graph)
        layered.layers.append(WaveformLayer(layered, graph))
        window.add(layered)
        window.show_all()
        gtk.main()

    def test_selection():
        window = gtk.Window()
        window.resize(500, 200)
        window.connect("delete-event", gtk.main_quit)

        graph = Mock({"get_values": [], "set_width": None,
                          "get_info": (0, 0, 0)})
        graph.changed = Fake()
        selection = Mock({"pixels": (20, 100)})
        selection.changed = Fake()
        layered = LayeredGraphView(graph)
        layered.layers.append(SelectionLayer(layered, graph, selection))
        window.add(layered)
        window.show_all()
        gtk.main()

    test_window()
    test_rand()
    test_sine()
    test_selection()
<commit_msg>scrollbar filled when sound is empty<commit_after>import gtk
import cairo

# -- Base classes for painting sound visualization.
#
# Defined step by step only for code clarity. Everything could as well
# be stuck in one class.

class CairoWidget(gtk.DrawingArea):

    __gsignals__ = {"expose-event": "override"}

    def __init__(self):
        super(CairoWidget, self).__init__()

    def do_expose_event(self, event):
        context = self.window.cairo_create()
        context.rectangle(event.area.x, event.area.y,
                          event.area.width, event.area.height)
        context.clip()
        width, height = self.window.get_size()
        self.draw(context, width, height)

    def update(self):
        # queue_draw() emits an expose event. Double buffering is used
        # automatically in the expose event handler.
        self.queue_draw()

    def draw(self, context, width, height):
        """Must be overriden to draw to the cairo context."""
        pass

class LayeredCairoWidget(CairoWidget):
    """A widget with several layers.

    This widget paints itself by successively passing its context to
    layer objects. The draw() method of a layer object must paint to
    the context.

    """
    def __init__(self):
        super(LayeredCairoWidget, self).__init__()
        self.layers = []

    def draw(self, context, width, height):
        for layer in self.layers:
            layer.draw(context, width, height)

class LayeredGraphView(LayeredCairoWidget):
    """A layered widget dedicated to paint data from a Graph
    object.

    As soon as it knows its width, it gives it to the Graph
    object.

    """
    def __init__(self, graph):
        super(LayeredGraphView, self).__init__()
        self._graph = graph
        self.connect("size_allocate", self.resize)

    def resize(self, widget, rect):
        self._graph.set_width(rect.width)

# -- Aah, beautiful sound visualization widget.

class GraphView(LayeredGraphView):
    """Sound visualization widget for the main window.

    * Two graphical layers: the waveform and the selection.
    * Mouse event listeners act on models (scroll and selection).

    """
    def __init__(self, graph, selection):
        super(GraphView, self).__init__(graph)
        self._graph = graph
        self._selection = selection
        self.layers.append(WaveformLayer(self, graph))
        self.layers.append(SelectionLayer(self, selection))
        MouseSelection(self, selection)
        MouseScroll(self, graph)

# -- The layers that can be added to LayeredGraphview.

class WaveformLayer(object):
    """A layer for LayeredGraphView.

    It paints the graph (the waveform). The cairo surface is cached,
    it is redrawn only when graph has changed.

    """
    def __init__(self, layered, graph):
        self._layered = layered
        self._graph = graph
        self._cache = None
        layered.add_events(gtk.gdk.SCROLL_MASK)
        graph.changed.connect(self.update)

    def update(self):
        self._cache = None
        self._layered.update()

    def draw(self, context, width, height):
        if not self._cache:
            surface = context.get_target()
            self._cache = surface.create_similar(cairo.CONTENT_COLOR,
                                                 width, height)
            c = cairo.Context(self._cache)

            # black background
            c.set_source_rgb(0, 0, 0)
            c.paint()

            # line at zero
            c.set_line_width(1)
            c.set_source_rgb(0.2, 0.2, 0.2)
            c.move_to(0, round(height / 2) + 0.5)
            c.line_to(width, round(height / 2) + 0.5)
            c.stroke()

            # waveform
            c.set_source_rgb(0, 0.9, 0)
            overview = self._graph.get_values()
            for i, value in enumerate(overview):
                x = i
                y = round((-value * 0.5 + 0.5) * height)
                #c.rectangle(x, y, 1, 1)
                #c.fill()
                c.move_to(x + 0.5, 0.5 * height + 0.5)
                c.line_to(x + 0.5, y + 0.5)
                c.stroke()

        context.set_source_surface(self._cache, 0, 0)
        context.set_operator(cairo.OPERATOR_SOURCE)
        context.paint()

class SelectionLayer(object):
    """A layer for LayeredGraphView.

    It highlights the selected area. It also listens to mouse events
    and changes the selection accordingly. The cairo surface is
    cached, it is redrawn only when selection has changed.

    """
    def __init__(self, layered, selection):
        self._layered = layered
        self._cache = None
        self._selection = selection
        self._selection.changed.connect(self.update)
        layered.add_events(gtk.gdk.BUTTON_PRESS_MASK |
                           gtk.gdk.BUTTON_RELEASE_MASK |
                           gtk.gdk.POINTER_MOTION_MASK |
                           gtk.gdk.POINTER_MOTION_HINT_MASK)

    def update(self):
        self._cache = None
        self._layered.update()

    def draw(self, context, width, height):
        if not self._cache:
            surface = context.get_target()
            self._cache = surface.create_similar(cairo.CONTENT_COLOR_ALPHA,
                                                 width, height)
            c = cairo.Context(self._cache)
            start, end = self._selection.pixels()

            if start != end:
                # darken everything ...
                c.set_source_rgba(0, 0, 0, 0.75)
                c.paint()

                # ... then clear selection
                c.set_operator(cairo.OPERATOR_CLEAR)
                c.rectangle(start + 0.5, 0, end - start + 0.5, height)
                c.fill()

            # cursors
            c.set_operator(cairo.OPERATOR_ADD)
            c.set_line_width(1)
            c.set_source_rgba(1, 1, 1, 0.7)
            c.move_to(start + 0.5, 0)
            c.line_to(start + 0.5, height)
            c.move_to(end + 0.5, 0)
            c.line_to(end + 0.5, height)
            c.stroke()

        context.set_source_surface(self._cache, 0, 0)
        context.set_operator(cairo.OPERATOR_OVER)
        context.paint()

# -- Mouse event listeners that act on models.

class MouseScroll(object):
    """Listens for mouse wheel events and scroll a graph

    Must be attached to a gtk.Widget and a Graph.

    """
    def __init__(self, widget, graph):
        self._graph = graph
        widget.connect("scroll_event", self.scroll_event)

    def scroll_event(self, widget, event):
        if event.direction in (gtk.gdk.SCROLL_UP, gtk.gdk.SCROLL_LEFT):
            self._graph.scroll_left()
        elif event.direction in (gtk.gdk.SCROLL_DOWN, gtk.gdk.SCROLL_RIGHT):
            self._graph.scroll_right()

class MouseSelection(object):
    """Listens for mouse events and select graph area

    Must be attached to a gtk.Widget and a Graph.

    """
    def __init__(self, widget, selection):
        self._selection = selection
        self.pressed = False
        widget.connect("button_press_event", self.button_press)
        widget.connect("button_release_event", self.button_release)
        widget.connect("motion_notify_event", self.motion_notify)

    def button_press(self, widget, event):
        if event.button == 1:
            self.pressed = True
            self._selection.start_selection(event.x)

    def motion_notify(self, widget, event):
        if self.pressed:
            x = event.window.get_pointer()[0]
            self._selection.end_selection(x)

    def button_release(self, widget, event):
        if event.button == 1:
            self.pressed = False

# -- An horizontal scrollbar, derived to control a graph model.

class GraphScrollbar(gtk.HScrollbar):
    """An horizontal scrollbar that acts on a Graph.

    Acts on a graph object and is updated when the graph
    object changes.

    """
    def __init__(self, graph):
        self._adjustment = gtk.Adjustment(0, 0, 1, 0.1, 0, 1)
        super(GraphScrollbar, self).__init__(self._adjustment)
        self._graph = graph
        self._graph.changed.connect(self.update_scrollbar)
        self.connect("value-changed", self.update_model)

        # When the scrollbar or the graph model changes, this
        # attribute must be set to True to avoid infinite feedback
        # between them. Example of what happens otherwise: scrollbar
        # changes --> graph changes --> scrollbar changes --> ...
        self.inhibit = False

    def update_model(self, widget):
        """Changes the model.

        Called when the scrollbar has been moved by the user.

        """
        if not self.inhibit:
            self.inhibit = True
            self._graph.view_starts_at(self._adjustment.value)
            self.inhibit = False

    def update_scrollbar(self):
        """Changes the scrollbar.

        Called when the model has changed.

        """
        if not self.inhibit:
            self.inhibit = True
            length, start, end = self._graph.get_info()
            if start != end:
                self._adjustment.upper = length
                self._adjustment.value = start
                self._adjustment.page_size = (end - start)
            else:
                # empty sound
                self._adjustment.upper = 1
                self._adjustment.value = 0
                self._adjustment.page_size = 1
            self.inhibit = False

# -- Testing

if __name__ == '__main__':
    from mock import Mock, Fake

    def test_window():
        window = gtk.Window()
        window.resize(500, 200)
        window.connect("delete-event", gtk.main_quit)
        graph = Mock({"get_values": [v / 500. for v in xrange(500)],
                     "set_width": None,
                     "get_info": (0, 0, 0)})
        graph.changed = Fake()
        layered = LayeredGraphView(graph)
        layered.layers.append(WaveformLayer(layered, graph))
        window.add(layered)
        window.show_all()
        gtk.main()

    def test_rand():
        window = gtk.Window()
        window.resize(500, 200)
        window.connect("delete-event", gtk.main_quit)

        from random import random
        values = [(random() - 0.5) * 2 for i in xrange(500)]
        graph = Mock({"get_values": values, "set_width": None,
                          "get_info": (0, 0, 0)})
        graph.changed = Fake()
        layered = LayeredGraphView(graph)
        layered.layers.append(WaveformLayer(layered, graph))
        window.add(layered)
        window.show_all()
        gtk.main()

    def test_sine():
        window = gtk.Window()
        window.resize(500, 200)
        window.connect("delete-event", gtk.main_quit)

        from math import sin
        sine = [sin(2 * 3.14 * 0.01 * x) for x in xrange(500)]
        graph = Mock({"get_values": sine, "set_width": None,
                          "get_info": (0, 0, 0)})
        graph.changed = Fake()
        layered = LayeredGraphView(graph)
        layered.layers.append(WaveformLayer(layered, graph))
        window.add(layered)
        window.show_all()
        gtk.main()

    def test_selection():
        window = gtk.Window()
        window.resize(500, 200)
        window.connect("delete-event", gtk.main_quit)

        graph = Mock({"get_values": [], "set_width": None,
                          "get_info": (0, 0, 0)})
        graph.changed = Fake()
        selection = Mock({"pixels": (20, 100)})
        selection.changed = Fake()
        layered = LayeredGraphView(graph)
        layered.layers.append(SelectionLayer(layered, graph, selection))
        window.add(layered)
        window.show_all()
        gtk.main()

    test_window()
    test_rand()
    test_sine()
    test_selection()
<|endoftext|>


<commit_before>from __future__ import division, print_function, absolute_import
from ._test_classifier import check_classifier
from sklearn.ensemble import BaggingClassifier
from rep.estimators.sklearn import SklearnClassifier
from rep.estimators.neurolab import NeurolabClassifier
import neurolab as nl

__author__ = 'Sterzhanov Vladislav'

N_EPOCHS = 150

def test_neurolab_single_classification():
    f = nl.trans.LogSig()
    f2 = nl.trans.SoftMax()
    check_classifier(NeurolabClassifier(show=0, layers=[], epochs=N_EPOCHS, trainf=nl.train.train_rprop),
                     supports_weight=False, has_staged_pp=False, has_importances=False)
    #check_classifier(NeurolabClassifier(net_type='single-layer', cn='auto', show=0, epochs=N_EPOCHS, trainf=nl.train.train_delta),
    #                 supports_weight=False, has_staged_pp=False, has_importances=False)
    #check_classifier(NeurolabClassifier(net_type='elman-recurrent', layers=[], show=0, epochs=N_EPOCHS, trainf=nl.train.train_gdx),
    #                 supports_weight=False, has_staged_pp=False, has_importances=False)

def test_neurolab_multiple_classification():
    check_classifier(NeurolabClassifier(show=0, layers=[], epochs=N_EPOCHS, trainf=nl.train.train_rprop),
                     supports_weight=False, has_staged_pp=False, has_importances=False, n_classes=3)
    #check_classifier(NeurolabClassifier(net_type='single-layer', cn='auto', show=0, epochs=N_EPOCHS, trainf=nl.train.train_delta),
    #                 supports_weight=False, has_staged_pp=False, has_importances=False, n_classes=3)
    #check_classifier(NeurolabClassifier(net_type='elman-recurrent', layers=[], show=0, epochs=N_EPOCHS, trainf=nl.train.train_gdx),
    #                 supports_weight=False, has_staged_pp=False, has_importances=False, n_classes=3)

def test_neurolab_stacking():
    base_nlab = NeurolabClassifier(show=0, layers=[], epochs=N_EPOCHS, trainf=nl.train.train_rprop)
    check_classifier(SklearnClassifier(clf=BaggingClassifier(base_estimator=base_nlab, n_estimators=3)),
                     supports_weight=False, has_staged_pp=False, has_importances=False)<commit_msg>new regression tests + regression param fixes<commit_after>from __future__ import division, print_function, absolute_import
from ._test_classifier import check_classifier, check_regression
from sklearn.ensemble import BaggingClassifier
from rep.estimators.sklearn import SklearnClassifier
from rep.estimators.neurolab import NeurolabClassifier, NeurolabRegressor
import neurolab as nl

__author__ = 'Sterzhanov Vladislav'

N_EPOCHS2 = 40
N_EPOCHS4 = 550
N_EPOCHS_REGR = 500

def test_neurolab_single_classification():
    check_classifier(NeurolabClassifier(show=0, layers=[], epochs=N_EPOCHS2, trainf=nl.train.train_rprop),
                     supports_weight=False, has_staged_pp=False, has_importances=False)
    check_classifier(NeurolabClassifier(net_type='single-layer', cn='auto', show=0, epochs=N_EPOCHS2, trainf=nl.train.train_delta),
                     supports_weight=False, has_staged_pp=False, has_importances=False)

def test_neurolab_multiple_classification():
    check_classifier(NeurolabClassifier(show=0, layers=[], epochs=N_EPOCHS4, trainf=nl.train.train_rprop),
                     supports_weight=False, has_staged_pp=False, has_importances=False, n_classes=4)
    check_classifier(NeurolabClassifier(net_type='single-layer', cn='auto', show=0, epochs=N_EPOCHS4, trainf=nl.train.train_delta),
                     supports_weight=False, has_staged_pp=False, has_importances=False, n_classes=4)

def test_neurolab_regression():
    check_regression(NeurolabRegressor(layers=[], show=0, epochs=N_EPOCHS_REGR, trainf=nl.train.train_rprop),
                     supports_weight=False, has_importances=False, has_staged_predictions=False)
    check_regression(NeurolabRegressor(net_type='single-layer', cn='auto', show=0, epochs=N_EPOCHS_REGR, trainf=nl.train.train_delta),
                     supports_weight=False, has_importances=False, has_staged_predictions=False)

def test_neurolab_stacking():
    base_nlab = NeurolabClassifier(show=0, layers=[], epochs=N_EPOCHS2, trainf=nl.train.train_rprop)
    check_classifier(SklearnClassifier(clf=BaggingClassifier(base_estimator=base_nlab, n_estimators=3)),
                     supports_weight=False, has_staged_pp=False, has_importances=False)<|endoftext|>