<commit_before>import os
from os.path import exists

import pytest

from pip._internal.locations import write_delete_marker_file
from pip._internal.status_codes import PREVIOUS_BUILD_DIR_ERROR
from tests.lib import need_mercurial
from tests.lib.local_repos import local_checkout

def test_cleanup_after_install(script, data):
    """
    Test clean up after installing a package.
    """
    script.pip(
        'install', '--no-index', '--find-links=%s' % data.find_links, 'simple'
    )
    build = script.venv_path / "build"
    src = script.venv_path / "src"
    assert not exists(build), "build/ dir still exists: %s" % build
    assert not exists(src), "unexpected src/ dir exists: %s" % src
    script.assert_no_temp()

@pytest.mark.network
def test_no_clean_option_blocks_cleaning_after_install(script, data):
    """
    Test --no-clean option blocks cleaning after install
    """
    build = script.base_path / 'pip-build'
    script.pip(
        'install', '--no-clean', '--no-index', '--build', build,
        '--find-links=%s' % data.find_links, 'simple', expect_temp=True,
    )
    assert exists(build)

@pytest.mark.network
@need_mercurial
def test_cleanup_after_install_editable_from_hg(script, tmpdir):
    """
    Test clean up after cloning from Mercurial.

    """
    script.pip(
        'install',
        '-e',
        '%s#egg=ScriptTest' %
        local_checkout(
            'hg+https://bitbucket.org/ianb/scripttest',
            tmpdir.join("cache"),
        ),
        expect_error=True,
    )
    build = script.venv_path / 'build'
    src = script.venv_path / 'src'
    assert not exists(build), "build/ dir still exists: %s" % build
    assert exists(src), "expected src/ dir doesn't exist: %s" % src
    script.assert_no_temp()

def test_cleanup_after_install_from_local_directory(script, data):
    """
    Test clean up after installing from a local directory.
    """
    to_install = data.packages.join("FSPkg")
    script.pip('install', to_install, expect_error=False)
    build = script.venv_path / 'build'
    src = script.venv_path / 'src'
    assert not exists(build), "unexpected build/ dir exists: %s" % build
    assert not exists(src), "unexpected src/ dir exist: %s" % src
    script.assert_no_temp()

def test_cleanup_req_satisifed_no_name(script, data):
    """
    Test cleanup when req is already satisfied, and req has no 'name'
    """
    # this test confirms Issue #420 is fixed
    # reqs with no 'name' that were already satisfied were leaving behind tmp
    # build dirs
    # 2 examples of reqs that would do this
    # 1) https://bitbucket.org/ianb/initools/get/tip.zip
    # 2) parent-0.1.tar.gz
    dist = data.packages.join("parent-0.1.tar.gz")

    script.pip('install', dist)
    script.pip('install', dist)

    build = script.venv_path / 'build'
    assert not exists(build), "unexpected build/ dir exists: %s" % build
    script.assert_no_temp()

def test_cleanup_after_install_exception(script, data):
    """
    Test clean up after a 'setup.py install' exception.
    """
    # broken==0.2broken fails during install; see packages readme file
    result = script.pip(
        'install', '-f', data.find_links, '--no-index', 'broken==0.2broken',
        expect_error=True,
    )
    build = script.venv_path / 'build'
    assert not exists(build), "build/ dir still exists: %s" % result.stdout
    script.assert_no_temp()

def test_cleanup_after_egg_info_exception(script, data):
    """
    Test clean up after a 'setup.py egg_info' exception.
    """
    # brokenegginfo fails during egg_info; see packages readme file
    result = script.pip(
        'install', '-f', data.find_links, '--no-index', 'brokenegginfo==0.1',
        expect_error=True,
    )
    build = script.venv_path / 'build'
    assert not exists(build), "build/ dir still exists: %s" % result.stdout
    script.assert_no_temp()

@pytest.mark.network
def test_cleanup_prevented_upon_build_dir_exception(script, data):
    """
    Test no cleanup occurs after a PreviousBuildDirError
    """
    build = script.venv_path / 'build'
    build_simple = build / 'simple'
    os.makedirs(build_simple)
    write_delete_marker_file(build)
    build_simple.join("setup.py").write("#")
    result = script.pip(
        'install', '-f', data.find_links, '--no-index', 'simple',
        '--build', build,
        expect_error=True, expect_temp=True,
    )

    assert result.returncode == PREVIOUS_BUILD_DIR_ERROR
    assert "pip can't proceed" in result.stderr
    assert exists(build_simple)
<commit_msg>tests: fix `test_cleanup_prevented_upon_build_dir_exception`<commit_after>import os
from os.path import exists

import pytest

from pip._internal.locations import write_delete_marker_file
from pip._internal.status_codes import PREVIOUS_BUILD_DIR_ERROR
from tests.lib import need_mercurial
from tests.lib.local_repos import local_checkout

def test_cleanup_after_install(script, data):
    """
    Test clean up after installing a package.
    """
    script.pip(
        'install', '--no-index', '--find-links=%s' % data.find_links, 'simple'
    )
    build = script.venv_path / "build"
    src = script.venv_path / "src"
    assert not exists(build), "build/ dir still exists: %s" % build
    assert not exists(src), "unexpected src/ dir exists: %s" % src
    script.assert_no_temp()

@pytest.mark.network
def test_no_clean_option_blocks_cleaning_after_install(script, data):
    """
    Test --no-clean option blocks cleaning after install
    """
    build = script.base_path / 'pip-build'
    script.pip(
        'install', '--no-clean', '--no-index', '--build', build,
        '--find-links=%s' % data.find_links, 'simple', expect_temp=True,
    )
    assert exists(build)

@pytest.mark.network
@need_mercurial
def test_cleanup_after_install_editable_from_hg(script, tmpdir):
    """
    Test clean up after cloning from Mercurial.

    """
    script.pip(
        'install',
        '-e',
        '%s#egg=ScriptTest' %
        local_checkout(
            'hg+https://bitbucket.org/ianb/scripttest',
            tmpdir.join("cache"),
        ),
        expect_error=True,
    )
    build = script.venv_path / 'build'
    src = script.venv_path / 'src'
    assert not exists(build), "build/ dir still exists: %s" % build
    assert exists(src), "expected src/ dir doesn't exist: %s" % src
    script.assert_no_temp()

def test_cleanup_after_install_from_local_directory(script, data):
    """
    Test clean up after installing from a local directory.
    """
    to_install = data.packages.join("FSPkg")
    script.pip('install', to_install, expect_error=False)
    build = script.venv_path / 'build'
    src = script.venv_path / 'src'
    assert not exists(build), "unexpected build/ dir exists: %s" % build
    assert not exists(src), "unexpected src/ dir exist: %s" % src
    script.assert_no_temp()

def test_cleanup_req_satisifed_no_name(script, data):
    """
    Test cleanup when req is already satisfied, and req has no 'name'
    """
    # this test confirms Issue #420 is fixed
    # reqs with no 'name' that were already satisfied were leaving behind tmp
    # build dirs
    # 2 examples of reqs that would do this
    # 1) https://bitbucket.org/ianb/initools/get/tip.zip
    # 2) parent-0.1.tar.gz
    dist = data.packages.join("parent-0.1.tar.gz")

    script.pip('install', dist)
    script.pip('install', dist)

    build = script.venv_path / 'build'
    assert not exists(build), "unexpected build/ dir exists: %s" % build
    script.assert_no_temp()

def test_cleanup_after_install_exception(script, data):
    """
    Test clean up after a 'setup.py install' exception.
    """
    # broken==0.2broken fails during install; see packages readme file
    result = script.pip(
        'install', '-f', data.find_links, '--no-index', 'broken==0.2broken',
        expect_error=True,
    )
    build = script.venv_path / 'build'
    assert not exists(build), "build/ dir still exists: %s" % result.stdout
    script.assert_no_temp()

def test_cleanup_after_egg_info_exception(script, data):
    """
    Test clean up after a 'setup.py egg_info' exception.
    """
    # brokenegginfo fails during egg_info; see packages readme file
    result = script.pip(
        'install', '-f', data.find_links, '--no-index', 'brokenegginfo==0.1',
        expect_error=True,
    )
    build = script.venv_path / 'build'
    assert not exists(build), "build/ dir still exists: %s" % result.stdout
    script.assert_no_temp()

@pytest.mark.network
def test_cleanup_prevented_upon_build_dir_exception(script, data):
    """
    Test no cleanup occurs after a PreviousBuildDirError
    """
    build = script.venv_path / 'build'
    build_simple = build / 'simple'
    os.makedirs(build_simple)
    write_delete_marker_file(build_simple)
    build_simple.join("setup.py").write("#")
    result = script.pip(
        'install', '-f', data.find_links, '--no-index', 'simple',
        '--build', build,
        expect_error=True, expect_temp=True,
    )

    assert result.returncode == PREVIOUS_BUILD_DIR_ERROR, str(result)
    assert "pip can't proceed" in result.stderr, str(result)
    assert exists(build_simple), str(result)
<|endoftext|>


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

from __future__ import absolute_import

import contextlib
import itertools

import numpy as np
import pytest
import six

import bezier
from bezier import _algebraic_intersection
from bezier import _geometric_intersection
from bezier import _surface_helpers
from bezier import curve
from tests.functional import utils

ALGEBRAIC = curve.IntersectionStrategy.ALGEBRAIC
GEOMETRIC = curve.IntersectionStrategy.GEOMETRIC
_, INTERSECTIONS = utils.surface_intersections_info()
PARALLEL_FAILURE = (_geometric_intersection._SEGMENTS_PARALLEL,)
BAD_TANGENT = (_surface_helpers._BAD_TANGENT,)
TOO_MANY = _geometric_intersection._TOO_MANY_TEMPLATE
WIGGLES = {
    GEOMETRIC: {
        1: 48,  # Established on CentOS 5 (i686 Docker image)
        3: 10,  # Established on AppVeyor (64-bit Python 2.7)
        8: 18,  # Established on CentOS 5 (i686 Docker image)
        13: 19,  # Established on Ubuntu 16.04
        22: 37,  # Established on Ubuntu 16.04
        32: 1013,  # Established on Ubuntu 16.04
        33: 1013,  # Established on Ubuntu 16.04
        34: 19,  # Established on Ubuntu 16.04 on CircleCI
    },
    ALGEBRAIC: {
        3: 12,  # Established on Ubuntu 16.04
        22: 14,  # Established on Ubuntu 16.04
        32: 18,  # Established on Ubuntu 16.04
        33: 18,  # Established on Ubuntu 16.04
    },
}
FAILED_CASES_TANGENT = {
    GEOMETRIC: {
        7: {'too_many': 66},
        10: {'parallel': True},
        11: {'too_many': 78},
        12: {'too_many': 76},
        21: {'bad_tangent': True},
    },
    ALGEBRAIC: {
        6: {},
        7: {},
        10: {},
        11: {},
        12: {},
        15: {},
        17: {},
        21: {},
    },
}
FAILED_CASES_COINCIDENT = {
    GEOMETRIC: {
        4: {'too_many': 96},
        5: {'parallel': True},
    },
    ALGEBRAIC: {
        4: {},
        5: {},
    },
}
CONFIG = utils.Config()

def find_edge_index(edge, root_edges):
    for index, root_edge in enumerate(root_edges):
        if root_edge.degree != edge.degree:
            continue
        specialized = root_edge.specialize(edge.start, edge.end)
        if np.allclose(specialized._nodes, edge._nodes):
            return index

    raise RuntimeError('No match found.')

def curved_polygon_edges(intersection, root_edges_pair):
    edges1, edges2 = root_edges_pair
    root_edges = edges1 + edges2
    # Re-sort the edges to be in the same order independent of strategy.
    edge_list = intersection._edges
    edge_info = [
        (find_edge_index(edge, root_edges), edge.start, edge.end)
        for edge in edge_list
    ]
    index = edge_info.index(min(edge_info))
    return edge_list[index:] + edge_list[:index]

@contextlib.contextmanager
def check_tangent_manager(
        strategy, parallel=False, bad_tangent=False, too_many=None):
    caught_exc = None
    try:
        yield
    except NotImplementedError as exc:
        caught_exc = exc

    assert caught_exc is not None
    exc_args = caught_exc.args
    if strategy is GEOMETRIC:
        if parallel:
            assert exc_args == PARALLEL_FAILURE
        elif bad_tangent:
            assert exc_args == BAD_TANGENT
        else:
            err_msg = TOO_MANY.format(too_many)
            assert exc_args == (err_msg,)
    else:
        assert len(exc_args) == 2
        assert exc_args[0] == _algebraic_intersection._NON_SIMPLE_ERR

@contextlib.contextmanager
def check_coincident_manager(strategy, parallel=False, too_many=None):
    caught_exc = None
    try:
        yield
    except NotImplementedError as exc:
        caught_exc = exc

    assert caught_exc is not None
    exc_args = caught_exc.args
    if strategy is GEOMETRIC:
        if parallel:
            assert exc_args == PARALLEL_FAILURE
        else:
            err_msg = TOO_MANY.format(too_many)
            assert exc_args == (err_msg,)
    else:
        assert exc_args == (_algebraic_intersection._COINCIDENT_ERR,)

def surface_surface_check(strategy, surface1, surface2, *all_intersected):
    # pylint: disable=too-many-locals
    assert surface1.is_valid
    assert surface2.is_valid

    intersections = surface1.intersect(surface2, strategy=strategy)
    assert len(intersections) == len(all_intersected)
    edges = (
        surface1._get_edges(),
        surface2._get_edges(),
    )

    for intersection, intersected in six.moves.zip(
            intersections, all_intersected):
        if isinstance(intersection, bezier.CurvedPolygon):
            assert isinstance(intersected, utils.CurvedPolygonInfo)
            start_vals = intersected.start_params
            end_vals = intersected.end_params
            nodes = intersected.nodes
            edge_pairs = intersected.edge_pairs

            int_edges = curved_polygon_edges(intersection, edges)
            info = six.moves.zip(
                int_edges, edge_pairs, start_vals, end_vals, nodes)
            num_edges = len(int_edges)
            assert num_edges == len(edge_pairs)
            assert num_edges == len(start_vals)
            assert num_edges == len(end_vals)
            assert num_edges == len(nodes)
            for edge, edge_pair, start_val, end_val, node in info:
                surf_index, edge_index = edge_pair
                expected_root = edges[surf_index][edge_index]
                specialized = expected_root.specialize(start_val, end_val)
                assert np.allclose(specialized._nodes, edge._nodes)

                CONFIG.assert_close(edge.start, start_val)
                CONFIG.assert_close(edge.end, end_val)
                CONFIG.assert_close(edge._nodes[0, 0], node[0])
                CONFIG.assert_close(edge._nodes[0, 1], node[1])
        else:
            assert isinstance(intersection, bezier.Surface)
            assert isinstance(intersected, utils.SurfaceIntersectionInfo)
            if intersected.first:
                assert intersection is surface1
            else:
                assert intersection is surface2
    # pylint: enable=too-many-locals

@pytest.mark.parametrize(
    'strategy,intersection_info',
    itertools.product(
        (GEOMETRIC, ALGEBRAIC),
        INTERSECTIONS,
    ),
    ids=utils.id_func,
)
def test_intersect(strategy, intersection_info):
    id_ = intersection_info.id_
    if id_ in FAILED_CASES_TANGENT[strategy]:
        kwargs = FAILED_CASES_TANGENT[strategy][id_]
        context = check_tangent_manager(strategy, **kwargs)
    elif id_ in FAILED_CASES_COINCIDENT[strategy]:
        kwargs = FAILED_CASES_COINCIDENT[strategy][id_]
        context = check_coincident_manager(strategy, **kwargs)
    elif id_ in WIGGLES[strategy]:
        context = CONFIG.wiggle(WIGGLES[strategy][id_])
    else:
        context = utils.no_op_manager()

    surface1 = intersection_info.surface1
    surface2 = intersection_info.surface2
    with context:
        surface_surface_check(
            strategy, surface1, surface2,
            *intersection_info.intersections)
<commit_msg>Allowing more wiggle room for surface-surface intersection 36.<commit_after># Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import absolute_import

import contextlib
import itertools

import numpy as np
import pytest
import six

import bezier
from bezier import _algebraic_intersection
from bezier import _geometric_intersection
from bezier import _surface_helpers
from bezier import curve
from tests.functional import utils

ALGEBRAIC = curve.IntersectionStrategy.ALGEBRAIC
GEOMETRIC = curve.IntersectionStrategy.GEOMETRIC
_, INTERSECTIONS = utils.surface_intersections_info()
PARALLEL_FAILURE = (_geometric_intersection._SEGMENTS_PARALLEL,)
BAD_TANGENT = (_surface_helpers._BAD_TANGENT,)
TOO_MANY = _geometric_intersection._TOO_MANY_TEMPLATE
WIGGLES = {
    GEOMETRIC: {
        1: 48,  # Established on CentOS 5 (i686 Docker image)
        3: 10,  # Established on AppVeyor (64-bit Python 2.7)
        8: 18,  # Established on CentOS 5 (i686 Docker image)
        13: 19,  # Established on Ubuntu 16.04
        22: 37,  # Established on Ubuntu 16.04
        32: 1013,  # Established on Ubuntu 16.04
        33: 1013,  # Established on Ubuntu 16.04
        34: 19,  # Established on Ubuntu 16.04 on CircleCI
    },
    ALGEBRAIC: {
        3: 12,  # Established on Ubuntu 16.04
        22: 14,  # Established on Ubuntu 16.04
        32: 18,  # Established on Ubuntu 16.04
        33: 18,  # Established on Ubuntu 16.04
        36: 9,  # Established on Ubuntu 16.04 on CircleCI and OS X.
    },
}
FAILED_CASES_TANGENT = {
    GEOMETRIC: {
        7: {'too_many': 66},
        10: {'parallel': True},
        11: {'too_many': 78},
        12: {'too_many': 76},
        21: {'bad_tangent': True},
    },
    ALGEBRAIC: {
        6: {},
        7: {},
        10: {},
        11: {},
        12: {},
        15: {},
        17: {},
        21: {},
    },
}
FAILED_CASES_COINCIDENT = {
    GEOMETRIC: {
        4: {'too_many': 96},
        5: {'parallel': True},
    },
    ALGEBRAIC: {
        4: {},
        5: {},
    },
}
CONFIG = utils.Config()

def find_edge_index(edge, root_edges):
    for index, root_edge in enumerate(root_edges):
        if root_edge.degree != edge.degree:
            continue
        specialized = root_edge.specialize(edge.start, edge.end)
        if np.allclose(specialized._nodes, edge._nodes):
            return index

    raise RuntimeError('No match found.')

def curved_polygon_edges(intersection, root_edges_pair):
    edges1, edges2 = root_edges_pair
    root_edges = edges1 + edges2
    # Re-sort the edges to be in the same order independent of strategy.
    edge_list = intersection._edges
    edge_info = [
        (find_edge_index(edge, root_edges), edge.start, edge.end)
        for edge in edge_list
    ]
    index = edge_info.index(min(edge_info))
    return edge_list[index:] + edge_list[:index]

@contextlib.contextmanager
def check_tangent_manager(
        strategy, parallel=False, bad_tangent=False, too_many=None):
    caught_exc = None
    try:
        yield
    except NotImplementedError as exc:
        caught_exc = exc

    assert caught_exc is not None
    exc_args = caught_exc.args
    if strategy is GEOMETRIC:
        if parallel:
            assert exc_args == PARALLEL_FAILURE
        elif bad_tangent:
            assert exc_args == BAD_TANGENT
        else:
            err_msg = TOO_MANY.format(too_many)
            assert exc_args == (err_msg,)
    else:
        assert len(exc_args) == 2
        assert exc_args[0] == _algebraic_intersection._NON_SIMPLE_ERR

@contextlib.contextmanager
def check_coincident_manager(strategy, parallel=False, too_many=None):
    caught_exc = None
    try:
        yield
    except NotImplementedError as exc:
        caught_exc = exc

    assert caught_exc is not None
    exc_args = caught_exc.args
    if strategy is GEOMETRIC:
        if parallel:
            assert exc_args == PARALLEL_FAILURE
        else:
            err_msg = TOO_MANY.format(too_many)
            assert exc_args == (err_msg,)
    else:
        assert exc_args == (_algebraic_intersection._COINCIDENT_ERR,)

def surface_surface_check(strategy, surface1, surface2, *all_intersected):
    # pylint: disable=too-many-locals
    assert surface1.is_valid
    assert surface2.is_valid

    intersections = surface1.intersect(surface2, strategy=strategy)
    assert len(intersections) == len(all_intersected)
    edges = (
        surface1._get_edges(),
        surface2._get_edges(),
    )

    for intersection, intersected in six.moves.zip(
            intersections, all_intersected):
        if isinstance(intersection, bezier.CurvedPolygon):
            assert isinstance(intersected, utils.CurvedPolygonInfo)
            start_vals = intersected.start_params
            end_vals = intersected.end_params
            nodes = intersected.nodes
            edge_pairs = intersected.edge_pairs

            int_edges = curved_polygon_edges(intersection, edges)
            info = six.moves.zip(
                int_edges, edge_pairs, start_vals, end_vals, nodes)
            num_edges = len(int_edges)
            assert num_edges == len(edge_pairs)
            assert num_edges == len(start_vals)
            assert num_edges == len(end_vals)
            assert num_edges == len(nodes)
            for edge, edge_pair, start_val, end_val, node in info:
                surf_index, edge_index = edge_pair
                expected_root = edges[surf_index][edge_index]
                specialized = expected_root.specialize(start_val, end_val)
                assert np.allclose(specialized._nodes, edge._nodes)

                CONFIG.assert_close(edge.start, start_val)
                CONFIG.assert_close(edge.end, end_val)
                CONFIG.assert_close(edge._nodes[0, 0], node[0])
                CONFIG.assert_close(edge._nodes[0, 1], node[1])
        else:
            assert isinstance(intersection, bezier.Surface)
            assert isinstance(intersected, utils.SurfaceIntersectionInfo)
            if intersected.first:
                assert intersection is surface1
            else:
                assert intersection is surface2
    # pylint: enable=too-many-locals

@pytest.mark.parametrize(
    'strategy,intersection_info',
    itertools.product(
        (GEOMETRIC, ALGEBRAIC),
        INTERSECTIONS,
    ),
    ids=utils.id_func,
)
def test_intersect(strategy, intersection_info):
    id_ = intersection_info.id_
    if id_ in FAILED_CASES_TANGENT[strategy]:
        kwargs = FAILED_CASES_TANGENT[strategy][id_]
        context = check_tangent_manager(strategy, **kwargs)
    elif id_ in FAILED_CASES_COINCIDENT[strategy]:
        kwargs = FAILED_CASES_COINCIDENT[strategy][id_]
        context = check_coincident_manager(strategy, **kwargs)
    elif id_ in WIGGLES[strategy]:
        context = CONFIG.wiggle(WIGGLES[strategy][id_])
    else:
        context = utils.no_op_manager()

    surface1 = intersection_info.surface1
    surface2 = intersection_info.surface2
    with context:
        surface_surface_check(
            strategy, surface1, surface2,
            *intersection_info.intersections)
<|endoftext|>


<commit_before>
import os
import subprocess
import numpy as np

def get_git_hash():
    cmd = ["git", "rev-parse", "--short", "HEAD"]
    return subprocess.check_output(cmd).strip()

# File IO

def makedirs_safe(dirname):
    if os.path.isdir(dirname):
        s = input('%s exists, overwrite? (y/n) ' % dirname)
        if s != 'y':
            raise Exception
    else:
        os.makedirs(dirname)

def makedirs_soft(dirname):
    if not os.path.isdir(dirname):
        os.makedirs(dirname)

# Index- and real-space conversion and wrapping

def r_to_i(r, L, dx):
    return np.asarray((r + L / 2.0) / dx, dtype=np.int)

def i_to_r(i, L, dx):
    return -L / 2.0 + (i + 0.5) * dx

# Centre of mass

def rms_com(r, L):
    ''' RMS distance of array of cartesian vectors r from their
    centre-of-mass vector.
    Assumes last index is that of the vector component. '''
    return rms(r, com(r, L), L)

def com(r, L):
    ''' Centre-of-mass vector of array of cartesian vectors r in
    a periodic system with period L.
    Assumes last index is that of the vector component. '''
    r_com = np.zeros([r.shape[-1]], dtype=np.float)
    for i_dim in range(len(r_com)):
        x_0 = r[:, i_dim]
        x_av = np.mean(x_0)
        rms_min = rms(x_0, x_av, L)
        steps_0 = 4
        steps = float(steps_0)
        while True:
            for x_base in np.arange(-L / 2.0 + L / steps, L / 2.0, L / steps):
                x_new = x_0.copy()
                x_new[np.where(x_new < x_base)] += L
                x_av_new = np.mean(x_new)
                if x_av_new > L / 2.0:
                    x_av_new -= L
                rms_new = rms(x_new, x_av_new, L)
                if rms_new < rms_min:
                    x_av = x_av_new
                    rms_min = rms_new

            if (rms(x_0, 0.99 * x_av, L) < rms_min or
                    rms(x_0, 1.01 * x_av, L) < rms_min):
                steps *= 2
                print('Recalculating c.o.m. with steps=%i' % steps)
            else:
                r_com[i_dim] = x_av
                break
    return r_com

def rms(r, r_0, L):
    ''' RMS distance of array of cartesian vectors r from point r_0.
    Assumes last index is that of the vector component (x, [y, z, ...]). '''
    r_sep_sq = np.minimum((r - r_0) ** 2, (L - np.abs(r - r_0)) ** 2)
    return np.sqrt(np.mean(np.sum(r_sep_sq, r_sep_sq.ndim - 1)))

# Vectors

def vector_mag_sq(v):
    ''' Squared magnitude of array of cartesian vectors v.
    Assumes last index is that of the vector component. '''
    return np.square(v).sum(axis=-1)

def vector_mag(v):
    ''' Magnitude of array of cartesian vectors v.
    Assumes last index is that of the vector component. '''
    return np.sqrt(vector_mag_sq(v))

def vector_unit_nonull(v):
    ''' Array of cartesian vectors v into unit vectors.
    If null vector encountered, raise exception.
    Assumes last index is that of the vector component. '''
    if v.size == 0:
        return v
    return v / vector_mag(v)[..., np.newaxis]

def vector_unit_nullnull(v):
    ''' Array of cartesian vectors into unit vectors.
    If null vector encountered, leave as null.
    Assumes last index is that of the vector component. '''
    if v.size == 0:
        return v
    mag = vector_mag(v)
    v_new = v.copy()
    v_new[mag > 0.0] /= mag[mag > 0.0][..., np.newaxis]
    return v_new

def vector_unit_nullrand(v):
    ''' Array of cartesian vectors into unit vectors.
    If null vector encountered, pick new random unit vector.
    Assumes last index is that of the vector component. '''
    if v.size == 0:
        return v
    mag = vector_mag(v)
    v_new = v.copy()
    v_new[mag == 0.0] = sphere_pick(v.shape[-1], (mag == 0.0).sum())
    v_new[mag > 0.0] /= mag[mag > 0.0][..., np.newaxis]
    return v_new

def vector_angle(a, b):
    cos_theta = np.sum(a * b, -1) / (vector_mag(a) * vector_mag(b))
    theta = np.empty_like(cos_theta)
    try:
        theta[np.abs(cos_theta) <= 1.0] = np.arccos(
            cos_theta[np.abs(cos_theta) <= 1.0])
    except IndexError:
        if np.abs(cos_theta) <= 1.0:
            theta = np.arccos(cos_theta)
        elif np.dot(a, b) > 0.0:
            theta = 0.0
        else:
            theta = np.pi
    else:
        for i in np.where(np.abs(cos_theta) > 1.0)[0]:
            if np.dot(a[i], b[i]) > 0.0:
                theta[i] = 0.0
            else:
                theta[i] = np.pi
    return theta

def vector_perp(v):
    ''' Vector perpendicular to 2D vector v '''
    if v.shape[-1] != 2:
        raise Exception('Can only define a unique perpendicular vector in 2d')
    v_perp = np.empty_like(v)
    v_perp[..., 0] = v[:, 1]
    v_perp[..., 1] = -v[:, 0]
    return v_perp

# Coordinate system transformations

def polar_to_cart(arr_p):
    ''' Array of vectors arr_c corresponding to cartesian
    representation of array of polar vectors arr_p.
    Assumes last index is that of the vector component.
    In 3d assumes (radius, inclination, azimuth) convention. '''
    if arr_p.shape[-1] == 1:
        arr_c = arr_p.copy()
    elif arr_p.shape[-1] == 2:
        arr_c = np.zeros_like(arr_p)
        arr_c[..., 0] = arr_p[..., 0] * np.cos(arr_p[..., 1])
        arr_c[..., 1] = arr_p[..., 0] * np.sin(arr_p[..., 1])
    elif arr_p.shape[-1] == 3:
        arr_c = np.zeros_like(arr_p)
        arr_c[..., 0] = arr_p[..., 0] * np.sin(
            arr_p[..., 1]) * np.cos(arr_p[..., 2])
        arr_c[..., 1] = arr_p[..., 0] * np.sin(
            arr_p[..., 1]) * np.sin(arr_p[..., 2])
        arr_c[..., 2] = arr_p[..., 0] * np.cos(arr_p[..., 1])
    else:
        raise Exception('Invalid vector for polar representation')
    return arr_c

def cart_to_polar(arr_c):
    ''' Array of vectors arr_p corresponding to polar representation
    of array of cartesian vectors arr_c.
    Assumes last index is that of the vector component.
    In 3d uses (radius, inclination, azimuth) convention. '''
    if arr_c.shape[-1] == 1:
        arr_p = arr_c.copy()
    elif arr_c.shape[-1] == 2:
        arr_p = np.zeros_like(arr_c)
        arr_p[..., 0] = vector_mag(arr_c)
        arr_p[..., 1] = np.arctan2(arr_c[..., 1], arr_c[..., 0])
    elif arr_c.shape[-1] == 3:
        arr_p = np.zeros_like(arr_c)
        arr_p[..., 0] = vector_mag(arr_c)
        arr_p[..., 1] = np.arccos(arr_c[..., 2] / arr_p[..., 0])
        arr_p[..., 2] = np.arctan2(arr_c[..., 1], arr_c[..., 0])
    else:
        raise Exception('Invalid vector for polar representation')
    return arr_p

# Point picking

def sphere_pick_polar(d, n=1):
    ''' In 3d uses (radius, inclination, azimuth) convention '''
    a = np.zeros([n, d], dtype=np.float)
    if d == 1:
        a[:, 0] = np.random.randint(2, size=n) * 2 - 1
    elif d == 2:
        a[:, 0] = 1.0
        a[:, 1] = np.random.uniform(-np.pi, +np.pi, n)
    elif d == 3:
        # Note, (r, theta, phi) notation
        u, v = np.random.uniform(0.0, 1.0, (2, n))
        a[:, 0] = 1.0
        a[:, 1] = np.arccos(2.0 * v - 1.0)
        a[:, 2] = 2.0 * np.pi * u
    else:
        raise Exception('Invalid vector for polar representation')
    return a

def sphere_pick(d, n=1):
    return polar_to_cart(sphere_pick_polar(d, n))

def disk_pick_polar(n=1):
    a = np.zeros([n, 2], dtype=np.float)
    a[:, 0] = np.sqrt(np.random.uniform(size=n))
    a[:, 1] = np.random.uniform(0.0, 2.0 * np.pi, size=n)
    return a

def disk_pick(n=1):
    return polar_to_cart(disk_pick_polar(n))

# Rotations

def R_rot_2d(th):
    s, = np.sin(th).T
    c, = np.cos(th).T
    R = np.zeros((len(th), 2, 2), dtype=np.float)
    R[:, 0, :] = np.array((c, -s)).T
    R[:, 1, :] = np.array((s, c)).T
    return R

def R_rot_3d(th):
    sx, sy, sz = np.sin(th).T
    cx, cy, cz = np.cos(th).T
    R = np.zeros((len(th), 3, 3), dtype=np.float)
    R[:, 0, :] = np.array((cy * cz, -cy * sz, sy)).T
    R[:, 1, :] = np.array(
        (sx * sy * cz + cx * sz, -sx * sy * sz + cx * cz, -sx * cy)).T
    R[:, 2, :] = np.array(
        (-cx * sy * cz + sx * sz, cx * sy * sz + sx * cz, cx * cy)).T
    return R

def R_rot(th):
    try:
        dof = th.shape[-1]
    # If th is a python float
    except AttributeError:
        dof = 1
        th = np.array([th])
    except IndexError:
        dof = 1
        th = np.array([th])
    # If th is a numpy float, i.e. 0d array
    if dof == 1:
        return R_rot_2d(th)
    elif dof == 3:
        return R_rot_3d(th)
    else:
        raise Exception('Rotation matrix not implemented in this dimension')

def rotate(a, th):
    return np.sum(a[..., np.newaxis] * R_rot(th), axis=-2)

# Diffusion

def rot_diff(v, D, dt):
    # Account for possibility of D being an array
    try:
        D = D[:, np.newaxis]
    # If D is a python float
    except TypeError:
        pass
    # If D is a numpy float, i.e. 0-d array
    except IndexError:
        pass
    angles = int(round(v.shape[-1] * (v.shape[-1] - 1) / 2.0))
    return rotate(v, np.sqrt(2.0 * D * dt) * np.random.standard_normal((len(v), angles)))

def diff(r, D, dt):
    if dt == 0.0:
        return r.copy()
    return r + np.sqrt(2.0 * D * dt) * np.random.standard_normal(r.shape)

# Arrays

def extend_array(a, n):
    a_new = a.copy()
    for d in range(a.ndim):
        a_new = np.repeat(a_new, n, axis=d)
    return a_new

def field_subset(f, inds, rank=0):
    f_dim_space = f.ndim - rank
    if inds.ndim > 2:
        raise Exception('Too many dimensions in indices array')
    if inds.ndim == 1:
        if f_dim_space == 1:
            return f[inds]
        else:
            raise Exception('Indices array is 1d but field is not')
    if inds.shape[1] != f_dim_space:
        raise Exception('Indices and field dimensions do not match')
    # It's magic, don't touch it!
    return f[tuple([inds[:, i] for i in range(inds.shape[1])])]
<commit_msg>Rewrite rotation functions to be faster and clearer<commit_after>
import os
import subprocess
import numpy as np

def get_git_hash():
    cmd = ["git", "rev-parse", "--short", "HEAD"]
    return subprocess.check_output(cmd).strip()

# File IO

def makedirs_safe(dirname):
    if os.path.isdir(dirname):
        s = input('%s exists, overwrite? (y/n) ' % dirname)
        if s != 'y':
            raise Exception
    else:
        os.makedirs(dirname)

def makedirs_soft(dirname):
    if not os.path.isdir(dirname):
        os.makedirs(dirname)

# Index- and real-space conversion and wrapping

def r_to_i(r, L, dx):
    return np.asarray((r + L / 2.0) / dx, dtype=np.int)

def i_to_r(i, L, dx):
    return -L / 2.0 + (i + 0.5) * dx

# Centre of mass

def rms_com(r, L):
    ''' RMS distance of array of cartesian vectors r from their
    centre-of-mass vector.
    Assumes last index is that of the vector component. '''
    return rms(r, com(r, L), L)

def com(r, L):
    ''' Centre-of-mass vector of array of cartesian vectors r in
    a periodic system with period L.
    Assumes last index is that of the vector component. '''
    r_com = np.zeros([r.shape[-1]], dtype=np.float)
    for i_dim in range(len(r_com)):
        x_0 = r[:, i_dim]
        x_av = np.mean(x_0)
        rms_min = rms(x_0, x_av, L)
        steps_0 = 4
        steps = float(steps_0)
        while True:
            for x_base in np.arange(-L / 2.0 + L / steps, L / 2.0, L / steps):
                x_new = x_0.copy()
                x_new[np.where(x_new < x_base)] += L
                x_av_new = np.mean(x_new)
                if x_av_new > L / 2.0:
                    x_av_new -= L
                rms_new = rms(x_new, x_av_new, L)
                if rms_new < rms_min:
                    x_av = x_av_new
                    rms_min = rms_new

            if (rms(x_0, 0.99 * x_av, L) < rms_min or
                    rms(x_0, 1.01 * x_av, L) < rms_min):
                steps *= 2
                print('Recalculating c.o.m. with steps=%i' % steps)
            else:
                r_com[i_dim] = x_av
                break
    return r_com

def rms(r, r_0, L):
    ''' RMS distance of array of cartesian vectors r from point r_0.
    Assumes last index is that of the vector component (x, [y, z, ...]). '''
    r_sep_sq = np.minimum((r - r_0) ** 2, (L - np.abs(r - r_0)) ** 2)
    return np.sqrt(np.mean(np.sum(r_sep_sq, r_sep_sq.ndim - 1)))

# Vectors

def vector_mag_sq(v):
    ''' Squared magnitude of array of cartesian vectors v.
    Assumes last index is that of the vector component. '''
    return np.square(v).sum(axis=-1)

def vector_mag(v):
    ''' Magnitude of array of cartesian vectors v.
    Assumes last index is that of the vector component. '''
    return np.sqrt(vector_mag_sq(v))

def vector_unit_nonull(v):
    ''' Array of cartesian vectors v into unit vectors.
    If null vector encountered, raise exception.
    Assumes last index is that of the vector component. '''
    if v.size == 0:
        return v
    return v / vector_mag(v)[..., np.newaxis]

def vector_unit_nullnull(v):
    ''' Array of cartesian vectors into unit vectors.
    If null vector encountered, leave as null.
    Assumes last index is that of the vector component. '''
    if v.size == 0:
        return v
    mag = vector_mag(v)
    v_new = v.copy()
    v_new[mag > 0.0] /= mag[mag > 0.0][..., np.newaxis]
    return v_new

def vector_unit_nullrand(v):
    ''' Array of cartesian vectors into unit vectors.
    If null vector encountered, pick new random unit vector.
    Assumes last index is that of the vector component. '''
    if v.size == 0:
        return v
    mag = vector_mag(v)
    v_new = v.copy()
    v_new[mag == 0.0] = sphere_pick(v.shape[-1], (mag == 0.0).sum())
    v_new[mag > 0.0] /= mag[mag > 0.0][..., np.newaxis]
    return v_new

def vector_angle(a, b):
    cos_theta = np.sum(a * b, -1) / (vector_mag(a) * vector_mag(b))
    theta = np.empty_like(cos_theta)
    try:
        theta[np.abs(cos_theta) <= 1.0] = np.arccos(
            cos_theta[np.abs(cos_theta) <= 1.0])
    except IndexError:
        if np.abs(cos_theta) <= 1.0:
            theta = np.arccos(cos_theta)
        elif np.dot(a, b) > 0.0:
            theta = 0.0
        else:
            theta = np.pi
    else:
        for i in np.where(np.abs(cos_theta) > 1.0)[0]:
            if np.dot(a[i], b[i]) > 0.0:
                theta[i] = 0.0
            else:
                theta[i] = np.pi
    return theta

def vector_perp(v):
    ''' Vector perpendicular to 2D vector v '''
    if v.shape[-1] != 2:
        raise Exception('Can only define a unique perpendicular vector in 2d')
    v_perp = np.empty_like(v)
    v_perp[..., 0] = v[:, 1]
    v_perp[..., 1] = -v[:, 0]
    return v_perp

# Coordinate system transformations

def polar_to_cart(arr_p):
    ''' Array of vectors arr_c corresponding to cartesian
    representation of array of polar vectors arr_p.
    Assumes last index is that of the vector component.
    In 3d assumes (radius, inclination, azimuth) convention. '''
    if arr_p.shape[-1] == 1:
        arr_c = arr_p.copy()
    elif arr_p.shape[-1] == 2:
        arr_c = np.zeros_like(arr_p)
        arr_c[..., 0] = arr_p[..., 0] * np.cos(arr_p[..., 1])
        arr_c[..., 1] = arr_p[..., 0] * np.sin(arr_p[..., 1])
    elif arr_p.shape[-1] == 3:
        arr_c = np.zeros_like(arr_p)
        arr_c[..., 0] = arr_p[..., 0] * np.sin(
            arr_p[..., 1]) * np.cos(arr_p[..., 2])
        arr_c[..., 1] = arr_p[..., 0] * np.sin(
            arr_p[..., 1]) * np.sin(arr_p[..., 2])
        arr_c[..., 2] = arr_p[..., 0] * np.cos(arr_p[..., 1])
    else:
        raise Exception('Invalid vector for polar representation')
    return arr_c

def cart_to_polar(arr_c):
    ''' Array of vectors arr_p corresponding to polar representation
    of array of cartesian vectors arr_c.
    Assumes last index is that of the vector component.
    In 3d uses (radius, inclination, azimuth) convention. '''
    if arr_c.shape[-1] == 1:
        arr_p = arr_c.copy()
    elif arr_c.shape[-1] == 2:
        arr_p = np.zeros_like(arr_c)
        arr_p[..., 0] = vector_mag(arr_c)
        arr_p[..., 1] = np.arctan2(arr_c[..., 1], arr_c[..., 0])
    elif arr_c.shape[-1] == 3:
        arr_p = np.zeros_like(arr_c)
        arr_p[..., 0] = vector_mag(arr_c)
        arr_p[..., 1] = np.arccos(arr_c[..., 2] / arr_p[..., 0])
        arr_p[..., 2] = np.arctan2(arr_c[..., 1], arr_c[..., 0])
    else:
        raise Exception('Invalid vector for polar representation')
    return arr_p

# Point picking

def sphere_pick_polar(d, n=1):
    ''' In 3d uses (radius, inclination, azimuth) convention '''
    a = np.zeros([n, d], dtype=np.float)
    if d == 1:
        a[:, 0] = np.random.randint(2, size=n) * 2 - 1
    elif d == 2:
        a[:, 0] = 1.0
        a[:, 1] = np.random.uniform(-np.pi, +np.pi, n)
    elif d == 3:
        # Note, (r, theta, phi) notation
        u, v = np.random.uniform(0.0, 1.0, (2, n))
        a[:, 0] = 1.0
        a[:, 1] = np.arccos(2.0 * v - 1.0)
        a[:, 2] = 2.0 * np.pi * u
    else:
        raise Exception('Invalid vector for polar representation')
    return a

def sphere_pick(d, n=1):
    return polar_to_cart(sphere_pick_polar(d, n))

def disk_pick_polar(n=1):
    a = np.zeros([n, 2], dtype=np.float)
    a[:, 0] = np.sqrt(np.random.uniform(size=n))
    a[:, 1] = np.random.uniform(0.0, 2.0 * np.pi, size=n)
    return a

def disk_pick(n=1):
    return polar_to_cart(disk_pick_polar(n))

# Rotations

def R_rot_2d(th):
    s, = np.sin(th).T
    c, = np.cos(th).T
    R = np.empty((len(th), 2, 2), dtype=np.float)

    R[:, 0, 0] = c
    R[:, 0, 1] = -s

    R[:, 1, 0] = s
    R[:, 1, 1] = c

    return R

def R_rot_3d(th):
    sx, sy, sz = np.sin(th).T
    cx, cy, cz = np.cos(th).T
    R = np.empty((len(th), 3, 3), dtype=np.float)

    R[:, 0, 0] = cy * cz
    R[:, 0, 1] = -cy * sz
    R[:, 0, 2] = sy

    R[:, 1, 0] = sx * sy * cz + cx * sz
    R[:, 1, 1] = -sx * sy * sz + cx * cz
    R[:, 1, 2] = -sx * cy

    R[:, 2, 0] = -cx * sy * cz + sx * sz
    R[:, 2, 1] = cx * sy * sz + sx * cz
    R[:, 2, 2] = cx * cy
    return R

def R_rot(th):
    try:
        dof = th.shape[-1]
    # If th is a python float
    except AttributeError:
        dof = 1
        th = np.array([th])
    except IndexError:
        dof = 1
        th = np.array([th])
    # If th is a numpy float, i.e. 0d array
    if dof == 1:
        return R_rot_2d(th)
    elif dof == 3:
        return R_rot_3d(th)
    else:
        raise Exception('Rotation matrix not implemented in this dimension')

def rotate(a, th):
    return np.sum(a[..., np.newaxis] * R_rot(th), axis=-2)

# Diffusion

def rot_diff(v, D, dt):
    # Account for possibility of D being an array
    try:
        D = D[:, np.newaxis]
    # If D is a python float
    except TypeError:
        pass
    # If D is a numpy float, i.e. 0-d array
    except IndexError:
        pass
    dim = v.shape[-1]
    dof = dim * (dim - 1) // 2
    th = np.sqrt(2.0 * D * dt) * np.random.standard_normal((len(v), dof))
    return rotate(v, th)

def diff(r, D, dt):
    if dt == 0.0:
        return r.copy()
    return r + np.sqrt(2.0 * D * dt) * np.random.standard_normal(r.shape)

# Arrays

def extend_array(a, n):
    a_new = a.copy()
    for d in range(a.ndim):
        a_new = np.repeat(a_new, n, axis=d)
    return a_new

def field_subset(f, inds, rank=0):
    f_dim_space = f.ndim - rank
    if inds.ndim > 2:
        raise Exception('Too many dimensions in indices array')
    if inds.ndim == 1:
        if f_dim_space == 1:
            return f[inds]
        else:
            raise Exception('Indices array is 1d but field is not')
    if inds.shape[1] != f_dim_space:
        raise Exception('Indices and field dimensions do not match')
    # It's magic, don't touch it!
    return f[tuple([inds[:, i] for i in range(inds.shape[1])])]
<|endoftext|>


<commit_before>from functools import partial
from http.cookies import SimpleCookie
import json
import mimetypes
import os.path
import random
import re
import string
import time
from urllib.parse import parse_qs

import bleach
import redis

ROOT = os.path.dirname(__file__)
POOL = redis.ConnectionPool()

RATE_LIMIT_DURATION = 60
RATE_LIMIT = 10

STATUS_OK = '200 OK'
STATUS_NOT_FOUND = '404 Not Found'
STATUS_METHOD_NOT_ALLOWED = '405 Method not allowed'
STATUS_RATE_LIMITED = '429 Too Many Requests'

def get_template(name):
    return open(os.path.join('templates/', name), 'rb')

make_key = lambda *args: ':'.join(args)

# Nick handling
def get_nicks(request):
    keys = request.conn.keys(make_key(request.channel, '*', 'nick'))
    return {
        k.decode('utf-8'): v.decode('utf-8')
        for k, v in zip(request.conn.mget(keys), keys)
    } if keys else {}

def get_nick(request):
    key = make_key(request.channel, request.tag, 'nick')
    nick = request.conn.get(key)
    if nick is None:
        nick = set_nick(request, request.tag[:8])
    else:
        nick = nick.decode('utf-8')
        request.conn.expire(key, 90)
    return nick

def set_nick(request, name):
    name = strip_tags(name)
    if name in get_nicks(request):
        raise ValueError('Nick in use!')
    key = make_key(request.channel, request.tag, 'nick')
    request.conn.set(key, name, ex=90)
    return name

# Topic handling
def set_topic(request, topic):
    key = make_key(request.channel, 'topic')
    request.conn.set(key, topic)

def get_topic(request):
    key = make_key(request.channel, 'topic')
    return request.conn.get(key).decode('utf-8')

# Message handling
def post_message(request, message, mode='message', queue=None, **data):
    if queue is None:
        queue = make_key(request.channel, 'channel')

    data.setdefault('message', message)
    data.setdefault('sender', get_nick(request))

    content = json.dumps(data)
    request.conn.publish(queue, json.dumps([mode, content]))

strip_tags = partial(bleach.clean, tags=[], strip=True)

def linkify_external(attrs, new=False):
    attrs['target'] = '_blank'
    return attrs

# The application!
class Request(object):
    def __init__(self, environ):
        self.environ = environ

        self.method = environ['REQUEST_METHOD']
        self.path = environ.get('PATH_INFO', '/')

        self.cookies = self.parse_cookies()
        self.query_data = self.parse_query_data()

        self.conn = redis.StrictRedis(connection_pool=POOL)

    def parse_cookies(self):
        cookie_data = self.environ.get('HTTP_COOKIE', '')
        cookies = SimpleCookie()
        if cookie_data:
            cookies.load(cookie_data)
        return {key: cookies.get(key).value for key in cookies.keys()}

    def parse_query_data(self):
        if self.method == 'POST':
            size = int(self.environ.get('CONTENT_LENGTH', 0))
            if not size:
                return {}
            src = parse_qs(self.environ['wsgi.input'].read(size))
        else:
            src = parse_qs(self.environ.get('QUERY_STRING', ''))
        return {k.decode('utf-8'): [x.decode('utf-8') for x in v]
                for k, v in src.items()}

class Response(object):
    def __init__(self, content=None, status=STATUS_OK, content_type=None):
        self.content = content or ''
        self.status = status
        self.headers = {'Content-Type': content_type or 'text/html'}
        self.cookies = SimpleCookie()

def application(environ, start_response):
    request = Request(environ)
    # Session cookie
    tag = request.cookies.get('chatterbox')
    request.tag = tag or ''.join(random.choice(string.ascii_letters)
                                 for x in range(16))
    # Rate limiting
    key = make_key(request.tag, 'rated')
    now = int(time.time())
    pipe = request.conn.pipeline(transaction=False)
    pipe.zadd(key, now, now).expireat(key, now + RATE_LIMIT_DURATION)
    pipe.zremrangebyscore(key, '-inf', now - RATE_LIMIT_DURATION)
    size = pipe.zcard(key).execute()[-1]
    if size > RATE_LIMIT:
        response = Response(status=STATUS_RATE_LIMITED)
    else:  # Dispatch
        response = Response(status=STATUS_NOT_FOUND)
        for pattern in URLPATTERNS:
            match = re.match(pattern[0], request.path)
            if match:
                response = pattern[1](request, **match.groupdict())

    if not tag:
        response.cookies['chatterbox'] = request.tag
        response.cookies['chatterbox']['path'] = b'/'

    headers = list(response.headers.items()) + [
        ('Set-Cookie', cookie.OutputString())
        for cookie in response.cookies.values()
    ]

    start_response(response.status, headers)
    return response.content

def index(request):
    return Response(get_template('chat.html'))

def chat(request, channel=None):
    request.channel = channel

    if request.method == 'GET':
        if 'text/event-stream' not in request.environ['HTTP_ACCEPT']:
            return Response(get_template('chat.html'))

        pubsub = request.conn.pubsub()
        pubsub.subscribe([
            make_key(request.channel, 'channel'),
            make_key(request.tag, 'private'),
        ])

        def _iterator():
            for msg in pubsub.listen():
                if msg['type'] == 'message':
                    mode, data = json.loads(msg['data'].decode('utf-8'))
                    yield 'event: {}\n'.format(mode).encode('utf-8')
                    for line in data.splitlines():
                        yield 'data: {}\n'.format(line).encode('utf-8')
                    yield '\n'.encode('utf-8')

        post_message(request, '{} connected.'.format(get_nick(request)),
                     'join', sender='Notice')

        response = Response(_iterator(), content_type='text/event-stream')
        response.headers['Cache-Control'] = 'no-cache'

    elif request.method == 'POST':

        nick = get_nick(request)

        mode = request.query_data.get('mode', ['message'])[0]
        msg = request.query_data.get('message', [''])[0]
        msg = bleach.linkify(strip_tags(msg), callbacks=[linkify_external])

        if mode == 'nick' and msg:
            try:
                new_nick = set_nick(request, msg)
            except ValueError:
                post_message(request, 'Nick in use!', 'alert', sender='Notice')
            else:
                post_message(request,
                             '{} is now known as {}'.format(nick, new_nick),
                             mode='nick',
                             sender='Notice')

        elif mode == 'names':
            post_message(request, list(get_nicks(request).keys()), 'names')

        elif mode == 'msg':
            target = request.query_data['target'][0]
            nicks = get_nicks(request)
            _, target_tag, _ = nicks[target].split(':')
            post_message(request, msg, 'msg', target=target,
                         queue=make_key(target_tag, 'private'))
            post_message(request, msg, 'msg', target=target,
                         queue=make_key(request.tag, 'private'))

        elif mode in ['message', 'action']:
            post_message(request, msg, mode)

        elif mode == 'topic':
            if msg:
                set_topic(request, msg)
            post_message(request, get_topic(request), 'topic')

        else:
            pass

        response = Response()

    else:
        response = Response(status=STATUS_METHOD_NOT_ALLOWED)

    return response

def static(request, filename):
    try:
        fin = open(os.path.join(ROOT, 'static/', filename), 'rb')
        ctype, _ = mimetypes.guess_type(filename)
        return Response(fin, content_type=ctype or 'application/octet-stream')
    except IOError:
        return Response(status=STATUS_NOT_FOUND)

URLPATTERNS = [
    (r'^/$', index, ),
    (r'^/static/(?P<filename>.*)$', static,),
    (r'^/(?P<channel>.+)/$', chat, ),
]
<commit_msg>Use http.client.responses for response messages<commit_after>from functools import partial
from http.cookies import SimpleCookie
from http.client import responses
import json
import mimetypes
import os.path
import random
import re
import string
import time
from urllib.parse import parse_qs

import bleach
import redis

ROOT = os.path.dirname(__file__)
POOL = redis.ConnectionPool()

RATE_LIMIT_DURATION = 60
RATE_LIMIT = 10

def get_template(name):
    return open(os.path.join('templates/', name), 'rb')

make_key = lambda *args: ':'.join(args)

# Nick handling
def get_nicks(request):
    keys = request.conn.keys(make_key(request.channel, '*', 'nick'))
    return {
        k.decode('utf-8'): v.decode('utf-8')
        for k, v in zip(request.conn.mget(keys), keys)
    } if keys else {}

def get_nick(request):
    key = make_key(request.channel, request.tag, 'nick')
    nick = request.conn.get(key)
    if nick is None:
        nick = set_nick(request, request.tag[:8])
    else:
        nick = nick.decode('utf-8')
        request.conn.expire(key, 90)
    return nick

def set_nick(request, name):
    name = strip_tags(name)
    if name in get_nicks(request):
        raise ValueError('Nick in use!')
    key = make_key(request.channel, request.tag, 'nick')
    request.conn.set(key, name, ex=90)
    return name

# Topic handling
def set_topic(request, topic):
    key = make_key(request.channel, 'topic')
    request.conn.set(key, topic)

def get_topic(request):
    key = make_key(request.channel, 'topic')
    return request.conn.get(key).decode('utf-8')

# Message handling
def post_message(request, message, mode='message', queue=None, **data):
    if queue is None:
        queue = make_key(request.channel, 'channel')

    data.setdefault('message', message)
    data.setdefault('sender', get_nick(request))

    content = json.dumps(data)
    request.conn.publish(queue, json.dumps([mode, content]))

strip_tags = partial(bleach.clean, tags=[], strip=True)

def linkify_external(attrs, new=False):
    attrs['target'] = '_blank'
    return attrs

# The application!
class Request(object):
    def __init__(self, environ):
        self.environ = environ

        self.method = environ['REQUEST_METHOD']
        self.path = environ.get('PATH_INFO', '/')

        self.cookies = self.parse_cookies()
        self.query_data = self.parse_query_data()

        self.conn = redis.StrictRedis(connection_pool=POOL)

    def parse_cookies(self):
        cookie_data = self.environ.get('HTTP_COOKIE', '')
        cookies = SimpleCookie()
        if cookie_data:
            cookies.load(cookie_data)
        return {key: cookies.get(key).value for key in cookies.keys()}

    def parse_query_data(self):
        if self.method == 'POST':
            size = int(self.environ.get('CONTENT_LENGTH', 0))
            if not size:
                return {}
            src = parse_qs(self.environ['wsgi.input'].read(size))
        else:
            src = parse_qs(self.environ.get('QUERY_STRING', ''))
        return {k.decode('utf-8'): [x.decode('utf-8') for x in v]
                for k, v in src.items()}

class Response(object):
    def __init__(self, content=None, status=200, content_type=None):
        self.content = content or []
        self.status = '{} {}'.format(status, responses[status])
        self.headers = {'Content-Type': content_type or 'text/html'}
        self.cookies = SimpleCookie()

def application(environ, start_response):
    request = Request(environ)
    # Session cookie
    tag = request.cookies.get('chatterbox')
    request.tag = tag or ''.join(random.choice(string.ascii_letters)
                                 for x in range(16))
    # Rate limiting
    key = make_key(request.tag, 'rated')
    now = int(time.time())
    pipe = request.conn.pipeline(transaction=False)
    pipe.zadd(key, now, now).expireat(key, now + RATE_LIMIT_DURATION)
    pipe.zremrangebyscore(key, '-inf', now - RATE_LIMIT_DURATION)
    size = pipe.zcard(key).execute()[-1]
    if size > RATE_LIMIT:
        response = Response(status=429)
    else:  # Dispatch
        response = Response(status=404)
        for pattern in URLPATTERNS:
            match = re.match(pattern[0], request.path)
            if match:
                response = pattern[1](request, **match.groupdict())

    if not tag:
        response.cookies['chatterbox'] = request.tag
        response.cookies['chatterbox']['path'] = b'/'

    headers = list(response.headers.items()) + [
        ('Set-Cookie', cookie.OutputString())
        for cookie in response.cookies.values()
    ]

    start_response(response.status, headers)
    return response.content

def index(request):
    return Response(get_template('chat.html'))

def chat(request, channel=None):
    request.channel = channel

    if request.method == 'GET':
        if 'text/event-stream' not in request.environ['HTTP_ACCEPT']:
            return Response(get_template('chat.html'))

        pubsub = request.conn.pubsub()
        pubsub.subscribe([
            make_key(request.channel, 'channel'),
            make_key(request.tag, 'private'),
        ])

        def _iterator():
            for msg in pubsub.listen():
                if msg['type'] == 'message':
                    mode, data = json.loads(msg['data'].decode('utf-8'))
                    yield 'event: {}\n'.format(mode).encode('utf-8')
                    for line in data.splitlines():
                        yield 'data: {}\n'.format(line).encode('utf-8')
                    yield '\n'.encode('utf-8')

        post_message(request, '{} connected.'.format(get_nick(request)),
                     'join', sender='Notice')

        response = Response(_iterator(), content_type='text/event-stream')
        response.headers['Cache-Control'] = 'no-cache'

    elif request.method == 'POST':

        nick = get_nick(request)

        mode = request.query_data.get('mode', ['message'])[0]
        msg = request.query_data.get('message', [''])[0]
        msg = bleach.linkify(strip_tags(msg), callbacks=[linkify_external])

        if mode == 'nick' and msg:
            try:
                new_nick = set_nick(request, msg)
            except ValueError:
                post_message(request, 'Nick in use!', 'alert', sender='Notice')
            else:
                post_message(request,
                             '{} is now known as {}'.format(nick, new_nick),
                             mode='nick',
                             sender='Notice')

        elif mode == 'names':
            post_message(request, list(get_nicks(request).keys()), 'names')

        elif mode == 'msg':
            target = request.query_data['target'][0]
            nicks = get_nicks(request)
            _, target_tag, _ = nicks[target].split(':')
            post_message(request, msg, 'msg', target=target,
                         queue=make_key(target_tag, 'private'))
            post_message(request, msg, 'msg', target=target,
                         queue=make_key(request.tag, 'private'))

        elif mode in ['message', 'action']:
            post_message(request, msg, mode)

        elif mode == 'topic':
            if msg:
                set_topic(request, msg)
            post_message(request, get_topic(request), 'topic')

        else:
            pass

        response = Response()

    else:
        response = Response(status=405)

    return response

def static(request, filename):
    try:
        fin = open(os.path.join(ROOT, 'static/', filename), 'rb')
        ctype, _ = mimetypes.guess_type(filename)
        return Response(fin, content_type=ctype or 'application/octet-stream')
    except IOError:
        return Response(status=404)

URLPATTERNS = [
    (r'^/$', index, ),
    (r'^/static/(?P<filename>.*)$', static,),
    (r'^/(?P<channel>.+)/$', chat, ),
]
<|endoftext|>


<commit_before>from __future__ import unicode_literals

from Cookie import SimpleCookie
from functools import partial
import json
import mimetypes
import os.path
import random
import re
import string
import time
from urlparse import parse_qs

import bleach
import redis

import logging

POOL = redis.ConnectionPool()

RATE_LIMIT_DURATION = 60
RATE_LIMIT = 10

STATUS_OK = '200 OK'
STATUS_NOT_FOUND = '404 Not Found'
STATUS_METHOD_NOT_ALLOWED = '405 Method not allowed'
STATUS_RATE_LIMITED = '429 Too Many Requests'

def get_template(name):
    with open(os.path.join('templates/', name), 'rb') as fin:
        return fin.read()

# Nick handling

make_key = lambda *args: u':'.join(args)

def get_nicks(request):
    keys = request.conn.keys(make_key(request.channel, '*', 'nick'))
    return dict(zip(request.conn.mget(keys), keys)) if keys else {}

def get_nick(request):
    key = make_key(request.channel, request.tag, 'nick')
    nick = request.conn.get(key)
    if nick is None:
        nick = set_nick(request, request.tag[:8])
    else:
        request.conn.expire(key, 90)
    return nick

def set_nick(request, name):
    name = strip_tags(name)
    if name in get_nicks(request):
        raise ValueError('Nick in use!')
    key = make_key(request.channel, request.tag, 'nick')
    request.conn.set(key, name, ex=90)
    return name

# Message handling

def post_message(request, message, mode='message', queue=None, **data):
    if queue is None:
        queue = make_key(request.channel, 'channel')

    data.setdefault('message', message)
    data.setdefault('sender', get_nick(request))

    content = json.dumps(data)
    request.conn.publish(queue, json.dumps([mode, content]))

strip_tags = partial(bleach.clean, tags=[], strip=True)

def linkify_external(attrs, new=False):
    attrs['target'] = '_blank'
    return attrs

# The application!

class Request(object):
    def __init__(self, environ):
        self.environ = environ

        self.method = environ['REQUEST_METHOD']
        self.path = environ.get('PATH_INFO', '/')

        self.cookies = self.parse_cookies()
        self.query_data = self.parse_query_data()

        self.conn = redis.StrictRedis(connection_pool=POOL)

    def parse_cookies(self):
        cookie_data = self.environ.get('HTTP_COOKIE', '')
        if cookie_data == '':
            return {}
        else:
            cookies = SimpleCookie()
            cookies.load(cookie_data)
            return {key: cookies.get(key).value for key in cookies.keys()}

    def parse_query_data(self):
        if self.method == 'GET':
            return parse_qs(self.environ.get('QUERY_STRING', ''))
        elif self.method == 'POST':
            # Should test content type
            size = int(self.environ.get('CONTENT_LENGTH', 0))
            if not size:
                return {}
            return parse_qs(self.environ['wsgi.input'].read(size))

class Response(object):
    def __init__(self, content=None, status=STATUS_OK, content_type=None):
        self.content = content or ''
        self.status = status
        self.headers = {}
        self.headers['Content-Type'] = content_type or 'text/html'
        self.cookies = SimpleCookie()

def application(environ, start_response):
    request = Request(environ)

    tag = request.cookies.get(b'chatterbox')
    if not tag:
        request.tag = ''.join(random.choice(string.letters + string.digits)
                              for x in range(16))
    else:
        request.tag = tag

    # Rate limiting
    key = make_key(request.tag, 'rated')
    now = int(time.time())
    pipe = request.conn.pipeline(transaction=False)
    pipe.zadd(key, now, now).expireat(key, now + RATE_LIMIT_DURATION)
    pipe.zremrangebyscore(key, '-inf', now - RATE_LIMIT_DURATION)
    size = pipe.zcard(key).execute()[-1]
    if size > RATE_LIMIT:
        response = Response(status=STATUS_RATE_LIMITED)

    else:
        # Dispatch
        response = Response(status=STATUS_NOT_FOUND)
        for pattern in URLPATTERNS:
            match = re.match(pattern[0], request.path)
            if match:
                response = pattern[1](request, **match.groupdict())

    if not tag:
        response.cookies[b'chatterbox'] = request.tag.encode('utf-8')
        response.cookies[b'chatterbox']['path'] = '/'

    headers = list(response.headers.items()) + [
        ('Set-Cookie', cookie.OutputString())
        for cookie in response.cookies.values()
    ]

    start_response(response.status, headers)
    return response.content

def index(request):
    return Response(get_template('index.html'))

def chat(request, channel=None):
    request.channel = channel

    if request.method == 'GET':
        if 'text/event-stream' not in request.environ['HTTP_ACCEPT']:
            return Response(get_template('chat.html'))

        pubsub = request.conn.pubsub()
        pubsub.subscribe([
            make_key(request.channel, 'channel'),
            make_key(request.tag, 'private'),
        ])

        def _iterator():
            for msg in pubsub.listen():
                if msg['type'] == 'message':
                    mode, data = json.loads(msg['data'])
                    yield u'event: {}\n'.format(mode).encode('utf-8')
                    for line in data.splitlines():
                        yield u'data: {}\n'.format(line).encode('utf-8')
                    yield u'\n'.encode('utf-8')

        post_message(request, '{} connected.'.format(get_nick(request)),
                     'join', sender='Notice')

        response = Response(_iterator(), content_type='text/event-stream')
        response.headers['Cache-Control'] = 'no-cache'

    elif request.method == 'POST':

        nick = get_nick(request)

        mode = request.query_data.get('mode', ['message'])[0]
        msg = request.query_data.get('message', [''])[0]
        msg = bleach.linkify(strip_tags(msg), callbacks=[linkify_external])

        if mode == 'nick' and msg:
            try:
                new_nick = set_nick(request, msg)
            except ValueError:
                post_message(request, 'Nick in use!', 'alert', sender='Notice')
            else:
                post_message(request,
                             '{} is now known as {}'.format(nick, new_nick),
                             mode='nick',
                             sender='Notice')

        elif mode == 'names':
            post_message(request, get_nicks(request).keys(), 'names')

        elif mode == 'msg':
            target = request.query_data['target'][0]
            nicks = get_nicks(request)
            _, target_tag, _ = nicks[target].split(':')
            post_message(request, msg, 'msg', target=target,
                         queue=make_key(target_tag, 'private'))
            post_message(request, msg, 'msg', target=target,
                         queue=make_key(request.tag, 'private'))

        elif mode in ['message', 'action']:
            post_message(request, msg, mode)

        else:
            logging.warning('Unknown message: %r', mode)

        response = Response()

    else:
        response = Response(status=STATUS_METHOD_NOT_ALLOWED)

    return response

def static(request, filename):
    try:
        fin = open(os.path.join('static/', filename), 'rb')
        content_type, encoding = mimetypes.guess_type(filename)
        content_type = content_type or 'application/octet-stream'
        return Response(fin.read(), content_type=content_type)
    except:
        return Response(status=STATUS_NOT_FOUND)

URLPATTERNS = [
    (r'^/$', index, ),
    (r'^/static/(?P<filename>.*)$', static,),
    (r'^/(?P<channel>.+)/$', chat, ),
]
<commit_msg>Start complying with obvious py3 differences<commit_after>
from functools import partial
from http.cookies import SimpleCookie
import json
import mimetypes
import os.path
import random
import re
import string
import time
from urllib.parse import parse_qs

import bleach
import redis

import logging

ROOT = os.path.dirname(__file__)
POOL = redis.ConnectionPool()

RATE_LIMIT_DURATION = 60
RATE_LIMIT = 10

STATUS_OK = '200 OK'
STATUS_NOT_FOUND = '404 Not Found'
STATUS_METHOD_NOT_ALLOWED = '405 Method not allowed'
STATUS_RATE_LIMITED = '429 Too Many Requests'

def get_template(name):
    return open(os.path.join('templates/', name), 'rb')

# Nick handling

make_key = lambda *args: u':'.join(args)

def get_nicks(request):
    keys = request.conn.keys(make_key(request.channel, '*', 'nick'))
    return dict(zip(request.conn.mget(keys), keys)) if keys else {}

def get_nick(request):
    key = make_key(request.channel, request.tag, 'nick')
    nick = request.conn.get(key)
    if nick is None:
        nick = set_nick(request, request.tag[:8])
    else:
        request.conn.expire(key, 90)
    return nick

def set_nick(request, name):
    name = strip_tags(name)
    if name in get_nicks(request):
        raise ValueError('Nick in use!')
    key = make_key(request.channel, request.tag, 'nick')
    request.conn.set(key, name, ex=90)
    return name

# Message handling

def post_message(request, message, mode='message', queue=None, **data):
    if queue is None:
        queue = make_key(request.channel, 'channel')

    data.setdefault('message', message)
    data.setdefault('sender', get_nick(request))

    content = json.dumps(data)
    request.conn.publish(queue, json.dumps([mode, content]))

strip_tags = partial(bleach.clean, tags=[], strip=True)

def linkify_external(attrs, new=False):
    attrs['target'] = '_blank'
    return attrs

# The application!

class Request(object):
    def __init__(self, environ):
        self.environ = environ

        self.method = environ['REQUEST_METHOD']
        self.path = environ.get('PATH_INFO', '/')

        self.cookies = self.parse_cookies()
        self.query_data = self.parse_query_data()

        self.conn = redis.StrictRedis(connection_pool=POOL)

    def parse_cookies(self):
        cookie_data = self.environ.get('HTTP_COOKIE', '')
        if cookie_data == '':
            return {}
        else:
            cookies = SimpleCookie()
            cookies.load(cookie_data)
            return {key: cookies.get(key).value for key in cookies.keys()}

    def parse_query_data(self):
        if self.method == 'GET':
            return parse_qs(self.environ.get('QUERY_STRING', ''))
        elif self.method == 'POST':
            # Should test content type
            size = int(self.environ.get('CONTENT_LENGTH', 0))
            if not size:
                return {}
            return parse_qs(self.environ['wsgi.input'].read(size))

class Response(object):
    def __init__(self, content=None, status=STATUS_OK, content_type=None):
        self.content = content or ''
        self.status = status
        self.headers = {}
        self.headers['Content-Type'] = content_type or 'text/html'
        self.cookies = SimpleCookie()

def application(environ, start_response):
    request = Request(environ)

    tag = request.cookies.get('chatterbox')
    if not tag:
        request.tag = ''.join(random.choice(string.ascii_letters + string.digits)
                              for x in range(16))
    else:
        request.tag = tag

    # Rate limiting
    key = make_key(request.tag, 'rated')
    now = int(time.time())
    pipe = request.conn.pipeline(transaction=False)
    pipe.zadd(key, now, now).expireat(key, now + RATE_LIMIT_DURATION)
    pipe.zremrangebyscore(key, '-inf', now - RATE_LIMIT_DURATION)
    size = pipe.zcard(key).execute()[-1]
    if size > RATE_LIMIT:
        response = Response(status=STATUS_RATE_LIMITED)

    else:
        # Dispatch
        response = Response(status=STATUS_NOT_FOUND)
        for pattern in URLPATTERNS:
            match = re.match(pattern[0], request.path)
            if match:
                response = pattern[1](request, **match.groupdict())

    if not tag:
        response.cookies['chatterbox'] = request.tag.encode('utf-8')
        response.cookies['chatterbox']['path'] = b'/'

    headers = list(response.headers.items()) + [
        ('Set-Cookie', cookie.OutputString())
        for cookie in response.cookies.values()
    ]

    start_response(response.status, headers)
    return response.content

def index(request):
    return Response(get_template('chat.html'))

def chat(request, channel=None):
    request.channel = channel

    if request.method == 'GET':
        if 'text/event-stream' not in request.environ['HTTP_ACCEPT']:
            return Response(get_template('chat.html'))

        pubsub = request.conn.pubsub()
        pubsub.subscribe([
            make_key(request.channel, 'channel'),
            make_key(request.tag, 'private'),
        ])

        def _iterator():
            for msg in pubsub.listen():
                if msg['type'] == 'message':
                    mode, data = json.loads(msg['data'])
                    yield u'event: {}\n'.format(mode).encode('utf-8')
                    for line in data.splitlines():
                        yield u'data: {}\n'.format(line).encode('utf-8')
                    yield u'\n'.encode('utf-8')

        post_message(request, '{} connected.'.format(get_nick(request)),
                     'join', sender='Notice')

        response = Response(_iterator(), content_type='text/event-stream')
        response.headers['Cache-Control'] = 'no-cache'

    elif request.method == 'POST':

        nick = get_nick(request)

        mode = request.query_data.get('mode', ['message'])[0]
        msg = request.query_data.get('message', [''])[0]
        msg = bleach.linkify(strip_tags(msg), callbacks=[linkify_external])

        if mode == 'nick' and msg:
            try:
                new_nick = set_nick(request, msg)
            except ValueError:
                post_message(request, 'Nick in use!', 'alert', sender='Notice')
            else:
                post_message(request,
                             '{} is now known as {}'.format(nick, new_nick),
                             mode='nick',
                             sender='Notice')

        elif mode == 'names':
            post_message(request, get_nicks(request).keys(), 'names')

        elif mode == 'msg':
            target = request.query_data['target'][0]
            nicks = get_nicks(request)
            _, target_tag, _ = nicks[target].split(':')
            post_message(request, msg, 'msg', target=target,
                         queue=make_key(target_tag, 'private'))
            post_message(request, msg, 'msg', target=target,
                         queue=make_key(request.tag, 'private'))

        elif mode in ['message', 'action']:
            post_message(request, msg, mode)

        else:
            logging.warning('Unknown message: %r', mode)

        response = Response()

    else:
        response = Response(status=STATUS_METHOD_NOT_ALLOWED)

    return response

def static(request, filename):
    try:
        fin = open(os.path.join(ROOT, 'static/', filename), 'rb')
        content_type, encoding = mimetypes.guess_type(filename)
        content_type = content_type or 'application/octet-stream'
        return Response(fin, content_type=content_type)
    except:
        return Response(status=STATUS_NOT_FOUND)

URLPATTERNS = [
    (r'^/$', index, ),
    (r'^/static/(?P<filename>.*)$', static,),
    (r'^/(?P<channel>.+)/$', chat, ),
]
<|endoftext|>


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

# Copyright (c) 2015 Intracom S.A. Telecom Solutions. All rights reserved.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v1.0 which accompanies this distribution,
# and is available at http://www.eclipse.org/legal/epl-v10.html

"""
Handler for requesting a Mininet REST server to start a Mininet topology on
the current node
"""
import json
import nb_gen
import requests
import sys

def northbound_generator():
    """
    Command line arguments:

    1.  ctrl_ip: controller IP
    2.  ctrl_port: controller RESTconf port
    3.  nnodes: number of nodes (switches) to generate operations for.
    Flows will be added to nodes [0, n-1]
    4.  nflows: total number of flows to distribute
    5.  nworkers: number of worker threads to create
    6.  flow_template: template from which flows are created
    7.  op_delay_ms: delay between thread operations (in milliseconds)
    8.  delete_flag: whether to delete or not the added flows as part of the
    test
    9.  discovery_deadline_ms: deadline for flow discovery (in milliseconds)
    10. auth_token: RESTconf authorization token (username/password tuple)
    """
    # Example
    # python3.4 nb_gen_handler "192.168.64.17" "8181" "100000" "10" "*.json" "100" "True" "240000" "admin" "admin"

    ctrl_ip = sys.argv[1]
    ctrl_port = sys.argv[2]
    nnodes = sys.argv[3]
    nflows = sys.argv[4]
    nworkers = sys.argv[5]
    flow_template = sys.argv[6]
    op_delay_ms = sys.argv[7]
    delete_flag = sys.argv[8]
    discovery_deadline_ms = sys.argv[9]
    auth_token = (sys.argv[10], sys.argv[11])
    print(nflows)
    nb_generator_results = flow_master_thread(ctrl_ip, ctrl_port, nnodes,
                                              nworkers, flow_template,
                                              op_delay_ms,
                                              delete_flag,
                                              discovery_deadline_ms,
                                              auth_token)
    print(json.dumps(nb_generator_results))

if __name__ == '__main__':
    northbound_generator()
<commit_msg>fixing call of flow_master_thread in handler of nb_generator<commit_after>#! /usr/bin/env python3.4

# Copyright (c) 2015 Intracom S.A. Telecom Solutions. All rights reserved.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v1.0 which accompanies this distribution,
# and is available at http://www.eclipse.org/legal/epl-v10.html

"""
Handler for requesting a Mininet REST server to start a Mininet topology on
the current node
"""
import json
import nb_gen
import requests
import sys

def northbound_generator():
    """
    Command line arguments:

    1.  ctrl_ip: controller IP
    2.  ctrl_port: controller RESTconf port
    3.  nnodes: number of nodes (switches) to generate operations for.
    Flows will be added to nodes [0, n-1]
    4.  nflows: total number of flows to distribute
    5.  nworkers: number of worker threads to create
    6.  flow_template: template from which flows are created
    7.  op_delay_ms: delay between thread operations (in milliseconds)
    8.  delete_flag: whether to delete or not the added flows as part of the
    test
    9.  discovery_deadline_ms: deadline for flow discovery (in milliseconds)
    10. auth_token: RESTconf authorization token (username/password tuple)
    """
    # Example
    # python3.4 nb_gen_handler "192.168.64.17" "8181" "100000" "10" "*.json" "100" "True" "240000" "admin" "admin"

    ctrl_ip = sys.argv[1]
    ctrl_port = sys.argv[2]
    nnodes = sys.argv[3]
    nflows = sys.argv[4]
    nworkers = sys.argv[5]
    flow_template = sys.argv[6]
    op_delay_ms = sys.argv[7]
    delete_flag = sys.argv[8]
    discovery_deadline_ms = sys.argv[9]
    auth_token = (sys.argv[10], sys.argv[11])
    print(nflows)
    nb_generator_results = nb_gen.flow_master_thread(ctrl_ip, ctrl_port, nnodes,
                                              nworkers, flow_template,
                                              op_delay_ms,
                                              delete_flag,
                                              discovery_deadline_ms,
                                              auth_token)
    print(json.dumps(nb_generator_results))

if __name__ == '__main__':
    northbound_generator()
<|endoftext|>


<commit_before>import datetime
import os
import pprint
import re
import time
import urllib2

from   bs4 import BeautifulSoup

def make_soup(url):
    try:
        output = urllib2.urlopen(url)
        html   = ''.join(output.readlines())
    except urllib2.HTTPError as ex:
        html = ''
        print 'x ',
    return BeautifulSoup(html)

def available_tickets():
    result = []
    sports = {
            'athletics'             : 8194,
            'basketball'            : 8198,
            'canoe slalom'          : 8202,
            'cycling - track'       : 8209,
            'diving'                : 8210,
            'gymnastics - artistic' : 8216,
            'gymnastics - rhythmic' : 8217,
            'hockey'                : 8219,
            'olympic park'          : 8269,
            'swimming'              : 8225,
            'volleyball'            : 8232,
            'waterpolo'             : 8233,
        }

    summary_template_url = 'http://www.tickets.london2012.com/browse?' + \
            'form=search&tab=oly&sport=%d&event=&venue=&fromDate=&' + \
            'toDate=&morning=1&afternoon=1&evening=1&' + \
            'show_available_events=1'
    details_template_url = 'http://www.tickets.london2012.com/' + \
            'eventdetails?id=%s'
    now = datetime.datetime.now()

    for sport, code in sports.iteritems():
        url    = summary_template_url % code
        soup   = make_soup(url)
        if soup.find('h2') and soup.find('h2').text == 'Search results':
            table = soup.find('table')
            if table:
                for elem in table.find_all('input', {'value':'Select'}):
                    row = elem.parent.parent.parent.parent
                    event_id  = row.find('input', {'name':'id'}).attrs['value']
                    details_url = details_template_url % event_id
                    details_soup = make_soup(details_url)
                    event_info = details_soup.find('table', {'title':'Ticket Summary'})
                    date_tag = details_soup.find('td', text='Date')
                    date = date_tag.nextSibling.nextSibling.text if date_tag else None
                    time_tag = details_soup.find('td', text='Time')
                    time = time_tag.nextSibling.nextSibling.text if time_tag else None
                    option_tags = details_soup.find_all('option', {'price':True})
                    if option_tags:
                        cost_texts = map(lambda x : x.text, option_tags)
                        costs = map(lambda x : re.findall('[0-9.]+', x)[0], cost_texts)
                        min_cost = min(map(lambda x : float(x), costs))
                        if min_cost <= 80:
                            result.append((sport, date, time, details_url))
    return result

if __name__ == "__main__":
    result = available_tickets()
    pprint.pprint(result) if result else None
    os.system('cvlc /usr/share/sounds/gnome/default/alerts/glass.ogg')
<commit_msg>properly killing vlc<commit_after>import datetime
import os
import pprint
import re
import time
import urllib2

from   bs4 import BeautifulSoup

def make_soup(url):
    try:
        output = urllib2.urlopen(url)
        html   = ''.join(output.readlines())
    except urllib2.HTTPError as ex:
        html = ''
        print 'x ',
    return BeautifulSoup(html)

def available_tickets():
    result = []
    sports = {
            'athletics'             : 8194,
            'basketball'            : 8198,
            'canoe slalom'          : 8202,
            'cycling - track'       : 8209,
            'diving'                : 8210,
            'gymnastics - artistic' : 8216,
            'gymnastics - rhythmic' : 8217,
            'hockey'                : 8219,
            'olympic park'          : 8269,
            'swimming'              : 8225,
            'volleyball'            : 8232,
            'waterpolo'             : 8233,
        }

    summary_template_url = 'http://www.tickets.london2012.com/browse?' + \
            'form=search&tab=oly&sport=%d&event=&venue=&fromDate=&' + \
            'toDate=&morning=1&afternoon=1&evening=1&' + \
            'show_available_events=1'
    details_template_url = 'http://www.tickets.london2012.com/' + \
            'eventdetails?id=%s'
    now = datetime.datetime.now()

    for sport, code in sports.iteritems():
        url    = summary_template_url % code
        soup   = make_soup(url)
        if soup.find('h2') and soup.find('h2').text == 'Search results':
            table = soup.find('table')
            if table:
                for elem in table.find_all('input', {'value':'Select'}):
                    row = elem.parent.parent.parent.parent
                    event_id  = row.find('input', {'name':'id'}).attrs['value']
                    details_url = details_template_url % event_id
                    details_soup = make_soup(details_url)
                    event_info = details_soup.find('table', {'title':'Ticket Summary'})
                    date_tag = details_soup.find('td', text='Date')
                    date = date_tag.nextSibling.nextSibling.text if date_tag else None
                    time_tag = details_soup.find('td', text='Time')
                    time = time_tag.nextSibling.nextSibling.text if time_tag else None
                    option_tags = details_soup.find_all('option', {'price':True})
                    if option_tags:
                        cost_texts = map(lambda x : x.text, option_tags)
                        costs = map(lambda x : re.findall('[0-9.]+', x)[0], cost_texts)
                        min_cost = min(map(lambda x : float(x), costs))
                        if min_cost <= 80:
                            result.append((sport, date, time, details_url))
    return result

if __name__ == "__main__":
    while True:
        time.sleep(2)
        print datetime.datetime.now().time()
        result = available_tickets()
        if result:
            break
        break
    pprint.pprint(result) if result else None
    os.system('cvlc /usr/share/sounds/gnome/default/alerts/glass.ogg vlc://quit')
<|endoftext|>


<commit_before>#  Copyright (c) 2021, CRS4
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy of
#  this software and associated documentation files (the "Software"), to deal in
#  the Software without restriction, including without limitation the rights to
#  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
#  the Software, and to permit persons to whom the Software is furnished to do so,
#  subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in all
#  copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
#  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
#  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
#  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
#  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from .. import settings

from uuid import uuid4
import os, zipfile, tarfile, tiledb, zarr
import logging

from .utils import switch_to_default_search_group

logger = logging.getLogger(__name__)

class InvalidDatasetPath(Exception):
    pass

class ArchiveFormatError(Exception):
    pass

class DatasetFormatError(Exception):
    pass

class DatasetPathAlreadyExistError(Exception):
    pass

def get_dataset_file_path(dataset_file_name):
    if settings.DATASETS_REPOSITORY is not None:
        dataset_path = os.path.join(settings.DATASETS_REPOSITORY, dataset_file_name)
        if os.path.isdir(dataset_path) or os.path.isfile(dataset_path):
            return dataset_path, os.path.isdir(dataset_path)
        else:
            raise InvalidDatasetPath('Path {0} not found'.format(dataset_path))
    else:
        raise settings.ServerConfigError('DATASETS default folder was not configured properly')

def _check_tiledb_dataset(dataset_path):
    logger.info('Checking dataset {0} for TILEDB compatibility'.format(dataset_path))
    try:
        x = tiledb.open(dataset_path)
        x.close()
        return 'dataset-folder/tiledb'
    except tiledb.TileDBError:
        return None

def _check_zarr_dataset(dataset_path):
    logger.info('Checking dataset {0} for ZARR compatibility'.format(dataset_path))
    x = zarr.open(dataset_path, 'r')
    if len(list(x.arrays())) > 0:
        if os.path.isdir(dataset_path):
            return 'dataset-folder/zarr'
        else:
            return 'dataset-archive/zarr'
    else:
        return None

def check_dataset(dataset_path, archive_dir):
    mtype = _check_zarr_dataset(dataset_path)
    if mtype is None:
        if archive_dir:
            mtype = _check_tiledb_dataset(dataset_path)
            if mtype is None:
                raise DatasetFormatError('Dataset {0} is not a valid TileDB or ZARR archive'.format(dataset_path))
        else:
            if zipfile.is_zipfile(dataset_path):
                mtype = 'application/zip'
            elif tarfile.is_tarfile(dataset_path):
                mtype = 'application/x-tar'
            else:
                raise DatasetFormatError('Dataset {0} is not a valid ZARR, ZIP or TAR archive'.format(dataset_path))
    return mtype

def extract(archive_handler, out_folder):
    dataset_label = str(uuid4())
    out_folder = dest_folder = os.path.join(out_folder, dataset_label)
    if not os.path.isdir(dest_folder):
        logger.info('Extracting archive to folder {0}'.format(out_folder))
        archive_handler.extractall(out_folder)
        logger.info('Dataset label: {0} -- Destination folder: {1}'.format(dataset_label, dest_folder))
        return dataset_label, dest_folder
    else:
        raise DatasetPathAlreadyExistError('Destination path {0} for archive extraction already exists'.format(dest_folder))

def extract_zip_archive(archive_file, out_folder):
    with zipfile.ZipFile(archive_file, 'r') as f:
        return extract(f, out_folder)

def extract_tar_archive(archive_file, out_folder):
    with tarfile.TarFile(archive_file, 'r') as f:
        return extract(f, out_folder)

def rename_archive(archive_file, out_folder=settings.DATASETS_REPOSITORY):
    _, ext = os.path.splitext(archive_file)
    new_dset_label = str(uuid4())
    new_fpath = os.path.join(out_folder, "{0}{1}".format(new_dset_label, ext))
    os.rename(archive_file, new_fpath)
    return new_dset_label, new_fpath

def extract_archive(archive_file, out_folder=settings.DATASETS_REPOSITORY, keep_archive=False):
    if zipfile.is_zipfile(archive_file):
        ds_label, dest_folder = extract_zip_archive(archive_file, out_folder)
    elif tarfile.is_tarfile(archive_file):
        ds_label, dest_folder = extract_tar_archive(archive_file, out_folder)
    else:
        raise ArchiveFormatError('Archive file {0} is nor ZIP or TAR format'.format(archive_file))
    logger.info('Archive extraction completed')
    if not keep_archive:
        logger.info('Deleting archive file')
        os.remove(archive_file)
    return ds_label, dest_folder

def _dataset_to_json(dataset_obj):
    return {
        'id': dataset_obj.getId(),
        'label': dataset_obj.getName(),
        'mimetype': dataset_obj.getMimetype()
    }

def get_datasets(connection):
    switch_to_default_search_group(connection)
    mtypes_filter = ['dataset-folder/tiledb', 'dataset-folder/zarr', 'dataset-archive/zarr']
    datasets = list()
    for mf in mtypes_filter:
        query_filter = {'mimetype': mf}
        datasets.extend(connection.getObjects('OriginalFile', attributes=query_filter))
    return [_dataset_to_json(d) for d in datasets]
<commit_msg>fix: tiledb registration bug fix<commit_after>#  Copyright (c) 2021, CRS4
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy of
#  this software and associated documentation files (the "Software"), to deal in
#  the Software without restriction, including without limitation the rights to
#  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
#  the Software, and to permit persons to whom the Software is furnished to do so,
#  subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in all
#  copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
#  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
#  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
#  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
#  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from .. import settings

from uuid import uuid4
import os, zipfile, tarfile, tiledb, zarr
import logging

from .utils import switch_to_default_search_group

logger = logging.getLogger(__name__)

class InvalidDatasetPath(Exception):
    pass

class ArchiveFormatError(Exception):
    pass

class DatasetFormatError(Exception):
    pass

class DatasetPathAlreadyExistError(Exception):
    pass

def get_dataset_file_path(dataset_file_name):
    if settings.DATASETS_REPOSITORY is not None:
        dataset_path = os.path.join(settings.DATASETS_REPOSITORY, dataset_file_name)
        if os.path.isdir(dataset_path) or os.path.isfile(dataset_path):
            return dataset_path, os.path.isdir(dataset_path)
        else:
            raise InvalidDatasetPath('Path {0} not found'.format(dataset_path))
    else:
        raise settings.ServerConfigError('DATASETS default folder was not configured properly')

def _check_tiledb_dataset(dataset_path):
    logger.info('Checking dataset {0} for TILEDB compatibility'.format(dataset_path))
    try:
        x = tiledb.open(dataset_path)
        x.close()
        return 'dataset-folder/tiledb'
    except tiledb.TileDBError:
        return None

def _check_zarr_dataset(dataset_path):
    logger.info('Checking dataset {0} for ZARR compatibility'.format(dataset_path))
    try:
        x = zarr.open(dataset_path, 'r')
        if len(list(x.arrays())) > 0:
            if os.path.isdir(dataset_path):
                return 'dataset-folder/zarr'
            else:
                return 'dataset-archive/zarr'
    except zarr.errors.PathNotFoundError:
        return None

def check_dataset(dataset_path, archive_dir):
    mtype = _check_zarr_dataset(dataset_path)
    if mtype is None:
        if archive_dir:
            mtype = _check_tiledb_dataset(dataset_path)
            if mtype is None:
                raise DatasetFormatError('Dataset {0} is not a valid TileDB or ZARR archive'.format(dataset_path))
        else:
            if zipfile.is_zipfile(dataset_path):
                mtype = 'application/zip'
            elif tarfile.is_tarfile(dataset_path):
                mtype = 'application/x-tar'
            else:
                raise DatasetFormatError('Dataset {0} is not a valid ZARR, ZIP or TAR archive'.format(dataset_path))
    return mtype

def extract(archive_handler, out_folder):
    dataset_label = str(uuid4())
    out_folder = dest_folder = os.path.join(out_folder, dataset_label)
    if not os.path.isdir(dest_folder):
        logger.info('Extracting archive to folder {0}'.format(out_folder))
        archive_handler.extractall(out_folder)
        logger.info('Dataset label: {0} -- Destination folder: {1}'.format(dataset_label, dest_folder))
        return dataset_label, dest_folder
    else:
        raise DatasetPathAlreadyExistError('Destination path {0} for archive extraction already exists'.format(dest_folder))

def extract_zip_archive(archive_file, out_folder):
    with zipfile.ZipFile(archive_file, 'r') as f:
        return extract(f, out_folder)

def extract_tar_archive(archive_file, out_folder):
    with tarfile.TarFile(archive_file, 'r') as f:
        return extract(f, out_folder)

def rename_archive(archive_file, out_folder=settings.DATASETS_REPOSITORY):
    _, ext = os.path.splitext(archive_file)
    new_dset_label = str(uuid4())
    new_fpath = os.path.join(out_folder, "{0}{1}".format(new_dset_label, ext))
    os.rename(archive_file, new_fpath)
    return new_dset_label, new_fpath

def extract_archive(archive_file, out_folder=settings.DATASETS_REPOSITORY, keep_archive=False):
    if zipfile.is_zipfile(archive_file):
        ds_label, dest_folder = extract_zip_archive(archive_file, out_folder)
    elif tarfile.is_tarfile(archive_file):
        ds_label, dest_folder = extract_tar_archive(archive_file, out_folder)
    else:
        raise ArchiveFormatError('Archive file {0} is nor ZIP or TAR format'.format(archive_file))
    logger.info('Archive extraction completed')
    if not keep_archive:
        logger.info('Deleting archive file')
        os.remove(archive_file)
    return ds_label, dest_folder

def _dataset_to_json(dataset_obj):
    return {
        'id': dataset_obj.getId(),
        'label': dataset_obj.getName(),
        'mimetype': dataset_obj.getMimetype()
    }

def get_datasets(connection):
    switch_to_default_search_group(connection)
    mtypes_filter = ['dataset-folder/tiledb', 'dataset-folder/zarr', 'dataset-archive/zarr']
    datasets = list()
    for mf in mtypes_filter:
        query_filter = {'mimetype': mf}
        datasets.extend(connection.getObjects('OriginalFile', attributes=query_filter))
    return [_dataset_to_json(d) for d in datasets]
<|endoftext|>