<commit_before>"""
Nose test running.

This module implements ``test()`` and ``bench()`` functions for NumPy modules.

"""
from __future__ import division, absolute_import, print_function

import os
import sys
import warnings
from numpy.compat import basestring
from numpy import ModuleDeprecationWarning

def get_package_name(filepath):
    """
    Given a path where a package is installed, determine its name.

    Parameters
    ----------
    filepath : str
        Path to a file. If the determination fails, "numpy" is returned.

    Examples
    --------
    >>> np.testing.nosetester.get_package_name('nonsense')
    'numpy'

    """

    fullpath = filepath[:]
    pkg_name = []
    while 'site-packages' in filepath or 'dist-packages' in filepath:
        filepath, p2 = os.path.split(filepath)
        if p2 in ('site-packages', 'dist-packages'):
            break
        pkg_name.append(p2)

    # if package name determination failed, just default to numpy/scipy
    if not pkg_name:
        if 'scipy' in fullpath:
            return 'scipy'
        else:
            return 'numpy'

    # otherwise, reverse to get correct order and return
    pkg_name.reverse()

    # don't include the outer egg directory
    if pkg_name[0].endswith('.egg'):
        pkg_name.pop(0)

    return '.'.join(pkg_name)

def import_nose():
    """ Import nose only when needed.
    """
    fine_nose = True
    minimum_nose_version = (0, 10, 0)
    try:
        import nose
    except ImportError:
        fine_nose = False
    else:
        if nose.__versioninfo__ < minimum_nose_version:
            fine_nose = False

    if not fine_nose:
        msg = 'Need nose >= %d.%d.%d for tests - see ' \
              'http://somethingaboutorange.com/mrl/projects/nose' % \
              minimum_nose_version

        raise ImportError(msg)

    return nose

def run_module_suite(file_to_run=None, argv=None):
    """
    Run a test module.

    Equivalent to calling ``$ nosetests <argv> <file_to_run>`` from
    the command line

    Parameters
    ----------
    file_to_run : str, optional
        Path to test module, or None.
        By default, run the module from which this function is called.
    argv : list of strings
        Arguments to be passed to the nose test runner. ``argv[0]`` is
        ignored. All command line arguments accepted by ``nosetests``
        will work. If it is the default value None, sys.argv is used.

        .. versionadded:: 1.9.0

    Examples
    --------
    Adding the following::

        if __name__ == "__main__" :
            run_module_suite(argv=sys.argv)

    at the end of a test module will run the tests when that module is
    called in the python interpreter.

    Alternatively, calling::

    >>> run_module_suite(file_to_run="numpy/tests/test_matlib.py")

    from an interpreter will run all the test routine in 'test_matlib.py'.
    """
    if file_to_run is None:
        f = sys._getframe(1)
        file_to_run = f.f_locals.get('__file__', None)
        if file_to_run is None:
            raise AssertionError

    if argv is None:
        argv = sys.argv + [file_to_run]
    else:
        argv = argv + [file_to_run]

    nose = import_nose()
    from .noseclasses import KnownFailure
    nose.run(argv=argv, addplugins=[KnownFailure()])

class NoseTester(object):
    """
    Nose test runner.

    This class is made available as numpy.testing.Tester, and a test function
    is typically added to a package's __init__.py like so::

      from numpy.testing import Tester
      test = Tester().test

    Calling this test function finds and runs all tests associated with the
    package and all its sub-packages.

    Attributes
    ----------
    package_path : str
        Full path to the package to test.
    package_name : str
        Name of the package to test.

    Parameters
    ----------
    package : module, str or None, optional
        The package to test. If a string, this should be the full path to
        the package. If None (default), `package` is set to the module from
        which `NoseTester` is initialized.
    raise_warnings : str or sequence of warnings, optional
        This specifies which warnings to configure as 'raise' instead
        of 'warn' during the test execution.  Valid strings are:

          - "develop" : equals ``(DeprecationWarning, RuntimeWarning)``
          - "release" : equals ``()``, don't raise on any warnings.

        See Notes for more details.

    Notes
    -----
    The default for `raise_warnings` is
    ``(DeprecationWarning, RuntimeWarning)`` for the master branch of NumPy,
    and ``()`` for maintenance branches and released versions.  The purpose
    of this switching behavior is to catch as many warnings as possible
    during development, but not give problems for packaging of released
    versions.

    """
    # Stuff to exclude from tests. These are from numpy.distutils
    excludes = ['f2py_ext',
                'f2py_f90_ext',
                'gen_ext',
                'pyrex_ext',
                'swig_ext']

    def __init__(self, package=None, raise_warnings="develop"):
        package_name = None
        if package is None:
            f = sys._getframe(1)
            package_path = f.f_locals.get('__file__', None)
            if package_path is None:
                raise AssertionError
            package_path = os.path.dirname(package_path)
            package_name = f.f_locals.get('__name__', None)
        elif isinstance(package, type(os)):
            package_path = os.path.dirname(package.__file__)
            package_name = getattr(package, '__name__', None)
        else:
            package_path = str(package)

        self.package_path = package_path

        # Find the package name under test; this name is used to limit coverage
        # reporting (if enabled).
        if package_name is None:
            package_name = get_package_name(package_path)
        self.package_name = package_name

        # Set to "release" in constructor in maintenance branches.
        self.raise_warnings = raise_warnings

    def _test_argv(self, label, verbose, extra_argv):
        ''' Generate argv for nosetest command

        Parameters
        ----------
        label : {'fast', 'full', '', attribute identifier}, optional
            see ``test`` docstring
        verbose : int, optional
            Verbosity value for test outputs, in the range 1-10. Default is 1.
        extra_argv : list, optional
            List with any extra arguments to pass to nosetests.

        Returns
        -------
        argv : list
            command line arguments that will be passed to nose
        '''
        argv = [__file__, self.package_path, '-s']
        if label and label != 'full':
            if not isinstance(label, basestring):
                raise TypeError('Selection label should be a string')
            if label == 'fast':
                label = 'not slow'
            argv += ['-A', label]
        argv += ['--verbosity', str(verbose)]

        # When installing with setuptools, and also in some other cases, the
        # test_*.py files end up marked +x executable. Nose, by default, does
        # not run files marked with +x as they might be scripts. However, in
        # our case nose only looks for test_*.py files under the package
        # directory, which should be safe.
        argv += ['--exe']

        if extra_argv:
            argv += extra_argv
        return argv

    def _show_system_info(self):
        nose = import_nose()

        import numpy
        print("NumPy version %s" % numpy.__version__)
        relaxed_strides = numpy.ones((10, 1), order="C").flags.f_contiguous
        print("NumPy relaxed strides checking option:", relaxed_strides)
        npdir = os.path.dirname(numpy.__file__)
        print("NumPy is installed in %s" % npdir)

        if 'scipy' in self.package_name:
            import scipy
            print("SciPy version %s" % scipy.__version__)
            spdir = os.path.dirname(scipy.__file__)
            print("SciPy is installed in %s" % spdir)

        pyversion = sys.version.replace('\n', '')
        print("Python version %s" % pyversion)
        print("nose version %d.%d.%d" % nose.__versioninfo__)

    def _get_custom_doctester(self):
        """ Return instantiated plugin for doctests

        Allows subclassing of this class to override doctester

        A return value of None means use the nose builtin doctest plugin
        """
        from .noseclasses import NumpyDoctest
        return NumpyDoctest()

    def prepare_test_args(self, label='fast', verbose=1, extra_argv=None,
                          doctests=False, coverage=False):
        """
        Run tests for module using nose.

        This method does the heavy lifting for the `test` method. It takes all
        the same arguments, for details see `test`.

        See Also
        --------
        test

        """
        # fail with nice error message if nose is not present
        import_nose()
        # compile argv
        argv = self._test_argv(label, verbose, extra_argv)
        # bypass tests noted for exclude
        for ename in self.excludes:
            argv += ['--exclude', ename]
        # our way of doing coverage
        if coverage:
            argv+=['--cover-package=%s' % self.package_name, '--with-coverage',
                   '--cover-tests', '--cover-erase']
        # construct list of plugins
        import nose.plugins.builtin
        from .noseclasses import KnownFailure, Unplugger
        plugins = [KnownFailure()]
        plugins += [p() for p in nose.plugins.builtin.plugins]
        # add doctesting if required
        doctest_argv = '--with-doctest' in argv
        if doctests == False and doctest_argv:
            doctests = True
        plug = self._get_custom_doctester()
        if plug is None:
            # use standard doctesting
            if doctests and not doctest_argv:
                argv += ['--with-doctest']
        else: # custom doctesting
            if doctest_argv: # in fact the unplugger would take care of this
                argv.remove('--with-doctest')
            plugins += [Unplugger('doctest'), plug]
            if doctests:
                argv += ['--with-' + plug.name]
        return argv, plugins

    def test(self, label='fast', verbose=1, extra_argv=None,
            doctests=False, coverage=False,
            raise_warnings=None):
        """
        Run tests for module using nose.

        Parameters
        ----------
        label : {'fast', 'full', '', attribute identifier}, optional
            Identifies the tests to run. This can be a string to pass to
            the nosetests executable with the '-A' option, or one of several
            special values.  Special values are:
            * 'fast' - the default - which corresponds to the ``nosetests -A``
              option of 'not slow'.
            * 'full' - fast (as above) and slow tests as in the
              'no -A' option to nosetests - this is the same as ''.
            * None or '' - run all tests.
            attribute_identifier - string passed directly to nosetests as '-A'.
        verbose : int, optional
            Verbosity value for test outputs, in the range 1-10. Default is 1.
        extra_argv : list, optional
            List with any extra arguments to pass to nosetests.
        doctests : bool, optional
            If True, run doctests in module. Default is False.
        coverage : bool, optional
            If True, report coverage of NumPy code. Default is False.
            (This requires the `coverage module:
             <http://nedbatchelder.com/code/modules/coverage.html>`_).
        raise_warnings : str or sequence of warnings, optional
            This specifies which warnings to configure as 'raise' instead
            of 'warn' during the test execution.  Valid strings are:

              - "develop" : equals ``(DeprecationWarning, RuntimeWarning)``
              - "release" : equals ``()``, don't raise on any warnings.

        Returns
        -------
        result : object
            Returns the result of running the tests as a
            ``nose.result.TextTestResult`` object.

        Notes
        -----
        Each NumPy module exposes `test` in its namespace to run all tests for it.
        For example, to run all tests for numpy.lib:

        >>> np.lib.test() #doctest: +SKIP

        Examples
        --------
        >>> result = np.lib.test() #doctest: +SKIP
        Running unit tests for numpy.lib
        ...
        Ran 976 tests in 3.933s

        OK

        >>> result.errors #doctest: +SKIP
        []
        >>> result.knownfail #doctest: +SKIP
        []
        """

        # cap verbosity at 3 because nose becomes *very* verbose beyond that
        verbose = min(verbose, 3)

        from . import utils
        utils.verbose = verbose

        if doctests:
            print("Running unit tests and doctests for %s" % self.package_name)
        else:
            print("Running unit tests for %s" % self.package_name)

        self._show_system_info()

        # reset doctest state on every run
        import doctest
        doctest.master = None

        if raise_warnings is None:
            raise_warnings = self.raise_warnings

        _warn_opts = dict(develop=(DeprecationWarning, RuntimeWarning),
                          release=())
        if raise_warnings in _warn_opts.keys():
            raise_warnings = _warn_opts[raise_warnings]

        with warnings.catch_warnings():
            # Reset the warning filters to the default state,
            # so that running the tests is more repeatable.
            warnings.resetwarnings()
            # If deprecation warnings are not set to 'error' below,
            # at least set them to 'warn'.
            warnings.filterwarnings('always', category=DeprecationWarning)
            # Force the requested warnings to raise
            for warningtype in raise_warnings:
                warnings.filterwarnings('error', category=warningtype)
            # Filter out annoying import messages.
            warnings.filterwarnings('ignore', message='Not importing directory')
            warnings.filterwarnings("ignore", message="numpy.dtype size changed")
            warnings.filterwarnings("ignore", message="numpy.ufunc size changed")
            warnings.filterwarnings("ignore", category=ModuleDeprecationWarning)
            warnings.filterwarnings("ignore", category=FutureWarning)
            # Filter out boolean '-' deprecation messages. This allows
            # older versions of scipy to test without a flood of messages.
            warnings.filterwarnings("ignore", message=".*boolean negative.*")
            warnings.filterwarnings("ignore", message=".*boolean subtract.*")

            from .noseclasses import NumpyTestProgram

            argv, plugins = self.prepare_test_args(
                    label, verbose, extra_argv, doctests, coverage)
            t = NumpyTestProgram(argv=argv, exit=False, plugins=plugins)

        return t.result

    def bench(self, label='fast', verbose=1, extra_argv=None):
        """
        Run benchmarks for module using nose.

        Parameters
        ----------
        label : {'fast', 'full', '', attribute identifier}, optional
            Identifies the benchmarks to run. This can be a string to pass to
            the nosetests executable with the '-A' option, or one of several
            special values.  Special values are:
            * 'fast' - the default - which corresponds to the ``nosetests -A``
              option of 'not slow'.
            * 'full' - fast (as above) and slow benchmarks as in the
              'no -A' option to nosetests - this is the same as ''.
            * None or '' - run all tests.
            attribute_identifier - string passed directly to nosetests as '-A'.
        verbose : int, optional
            Verbosity value for benchmark outputs, in the range 1-10. Default is 1.
        extra_argv : list, optional
            List with any extra arguments to pass to nosetests.

        Returns
        -------
        success : bool
            Returns True if running the benchmarks works, False if an error
            occurred.

        Notes
        -----
        Benchmarks are like tests, but have names starting with "bench" instead
        of "test", and can be found under the "benchmarks" sub-directory of the
        module.

        Each NumPy module exposes `bench` in its namespace to run all benchmarks
        for it.

        Examples
        --------
        >>> success = np.lib.bench() #doctest: +SKIP
        Running benchmarks for numpy.lib
        ...
        using 562341 items:
        unique:
        0.11
        unique1d:
        0.11
        ratio: 1.0
        nUnique: 56230 == 56230
        ...
        OK

        >>> success #doctest: +SKIP
        True

        """

        print("Running benchmarks for %s" % self.package_name)
        self._show_system_info()

        argv = self._test_argv(label, verbose, extra_argv)
        argv += ['--match', r'(?:^|[\\b_\\.%s-])[Bb]ench' % os.sep]

        # import nose or make informative error
        nose = import_nose()

        # get plugin to disable doctests
        from .noseclasses import Unplugger
        add_plugins = [Unplugger('doctest')]

        return nose.run(argv=argv, addplugins=add_plugins)
<commit_msg>MAINT: filter out some warnings triggered by nose 1.3.7 + python 3.5b2<commit_after>"""
Nose test running.

This module implements ``test()`` and ``bench()`` functions for NumPy modules.

"""
from __future__ import division, absolute_import, print_function

import os
import sys
import warnings
from numpy.compat import basestring
from numpy import ModuleDeprecationWarning

def get_package_name(filepath):
    """
    Given a path where a package is installed, determine its name.

    Parameters
    ----------
    filepath : str
        Path to a file. If the determination fails, "numpy" is returned.

    Examples
    --------
    >>> np.testing.nosetester.get_package_name('nonsense')
    'numpy'

    """

    fullpath = filepath[:]
    pkg_name = []
    while 'site-packages' in filepath or 'dist-packages' in filepath:
        filepath, p2 = os.path.split(filepath)
        if p2 in ('site-packages', 'dist-packages'):
            break
        pkg_name.append(p2)

    # if package name determination failed, just default to numpy/scipy
    if not pkg_name:
        if 'scipy' in fullpath:
            return 'scipy'
        else:
            return 'numpy'

    # otherwise, reverse to get correct order and return
    pkg_name.reverse()

    # don't include the outer egg directory
    if pkg_name[0].endswith('.egg'):
        pkg_name.pop(0)

    return '.'.join(pkg_name)

def import_nose():
    """ Import nose only when needed.
    """
    fine_nose = True
    minimum_nose_version = (0, 10, 0)
    try:
        import nose
    except ImportError:
        fine_nose = False
    else:
        if nose.__versioninfo__ < minimum_nose_version:
            fine_nose = False

    if not fine_nose:
        msg = 'Need nose >= %d.%d.%d for tests - see ' \
              'http://somethingaboutorange.com/mrl/projects/nose' % \
              minimum_nose_version

        raise ImportError(msg)

    return nose

def run_module_suite(file_to_run=None, argv=None):
    """
    Run a test module.

    Equivalent to calling ``$ nosetests <argv> <file_to_run>`` from
    the command line

    Parameters
    ----------
    file_to_run : str, optional
        Path to test module, or None.
        By default, run the module from which this function is called.
    argv : list of strings
        Arguments to be passed to the nose test runner. ``argv[0]`` is
        ignored. All command line arguments accepted by ``nosetests``
        will work. If it is the default value None, sys.argv is used.

        .. versionadded:: 1.9.0

    Examples
    --------
    Adding the following::

        if __name__ == "__main__" :
            run_module_suite(argv=sys.argv)

    at the end of a test module will run the tests when that module is
    called in the python interpreter.

    Alternatively, calling::

    >>> run_module_suite(file_to_run="numpy/tests/test_matlib.py")

    from an interpreter will run all the test routine in 'test_matlib.py'.
    """
    if file_to_run is None:
        f = sys._getframe(1)
        file_to_run = f.f_locals.get('__file__', None)
        if file_to_run is None:
            raise AssertionError

    if argv is None:
        argv = sys.argv + [file_to_run]
    else:
        argv = argv + [file_to_run]

    nose = import_nose()
    from .noseclasses import KnownFailure
    nose.run(argv=argv, addplugins=[KnownFailure()])

class NoseTester(object):
    """
    Nose test runner.

    This class is made available as numpy.testing.Tester, and a test function
    is typically added to a package's __init__.py like so::

      from numpy.testing import Tester
      test = Tester().test

    Calling this test function finds and runs all tests associated with the
    package and all its sub-packages.

    Attributes
    ----------
    package_path : str
        Full path to the package to test.
    package_name : str
        Name of the package to test.

    Parameters
    ----------
    package : module, str or None, optional
        The package to test. If a string, this should be the full path to
        the package. If None (default), `package` is set to the module from
        which `NoseTester` is initialized.
    raise_warnings : str or sequence of warnings, optional
        This specifies which warnings to configure as 'raise' instead
        of 'warn' during the test execution.  Valid strings are:

          - "develop" : equals ``(DeprecationWarning, RuntimeWarning)``
          - "release" : equals ``()``, don't raise on any warnings.

        See Notes for more details.

    Notes
    -----
    The default for `raise_warnings` is
    ``(DeprecationWarning, RuntimeWarning)`` for the master branch of NumPy,
    and ``()`` for maintenance branches and released versions.  The purpose
    of this switching behavior is to catch as many warnings as possible
    during development, but not give problems for packaging of released
    versions.

    """
    # Stuff to exclude from tests. These are from numpy.distutils
    excludes = ['f2py_ext',
                'f2py_f90_ext',
                'gen_ext',
                'pyrex_ext',
                'swig_ext']

    def __init__(self, package=None, raise_warnings="develop"):
        package_name = None
        if package is None:
            f = sys._getframe(1)
            package_path = f.f_locals.get('__file__', None)
            if package_path is None:
                raise AssertionError
            package_path = os.path.dirname(package_path)
            package_name = f.f_locals.get('__name__', None)
        elif isinstance(package, type(os)):
            package_path = os.path.dirname(package.__file__)
            package_name = getattr(package, '__name__', None)
        else:
            package_path = str(package)

        self.package_path = package_path

        # Find the package name under test; this name is used to limit coverage
        # reporting (if enabled).
        if package_name is None:
            package_name = get_package_name(package_path)
        self.package_name = package_name

        # Set to "release" in constructor in maintenance branches.
        self.raise_warnings = raise_warnings

    def _test_argv(self, label, verbose, extra_argv):
        ''' Generate argv for nosetest command

        Parameters
        ----------
        label : {'fast', 'full', '', attribute identifier}, optional
            see ``test`` docstring
        verbose : int, optional
            Verbosity value for test outputs, in the range 1-10. Default is 1.
        extra_argv : list, optional
            List with any extra arguments to pass to nosetests.

        Returns
        -------
        argv : list
            command line arguments that will be passed to nose
        '''
        argv = [__file__, self.package_path, '-s']
        if label and label != 'full':
            if not isinstance(label, basestring):
                raise TypeError('Selection label should be a string')
            if label == 'fast':
                label = 'not slow'
            argv += ['-A', label]
        argv += ['--verbosity', str(verbose)]

        # When installing with setuptools, and also in some other cases, the
        # test_*.py files end up marked +x executable. Nose, by default, does
        # not run files marked with +x as they might be scripts. However, in
        # our case nose only looks for test_*.py files under the package
        # directory, which should be safe.
        argv += ['--exe']

        if extra_argv:
            argv += extra_argv
        return argv

    def _show_system_info(self):
        nose = import_nose()

        import numpy
        print("NumPy version %s" % numpy.__version__)
        relaxed_strides = numpy.ones((10, 1), order="C").flags.f_contiguous
        print("NumPy relaxed strides checking option:", relaxed_strides)
        npdir = os.path.dirname(numpy.__file__)
        print("NumPy is installed in %s" % npdir)

        if 'scipy' in self.package_name:
            import scipy
            print("SciPy version %s" % scipy.__version__)
            spdir = os.path.dirname(scipy.__file__)
            print("SciPy is installed in %s" % spdir)

        pyversion = sys.version.replace('\n', '')
        print("Python version %s" % pyversion)
        print("nose version %d.%d.%d" % nose.__versioninfo__)

    def _get_custom_doctester(self):
        """ Return instantiated plugin for doctests

        Allows subclassing of this class to override doctester

        A return value of None means use the nose builtin doctest plugin
        """
        from .noseclasses import NumpyDoctest
        return NumpyDoctest()

    def prepare_test_args(self, label='fast', verbose=1, extra_argv=None,
                          doctests=False, coverage=False):
        """
        Run tests for module using nose.

        This method does the heavy lifting for the `test` method. It takes all
        the same arguments, for details see `test`.

        See Also
        --------
        test

        """
        # fail with nice error message if nose is not present
        import_nose()
        # compile argv
        argv = self._test_argv(label, verbose, extra_argv)
        # bypass tests noted for exclude
        for ename in self.excludes:
            argv += ['--exclude', ename]
        # our way of doing coverage
        if coverage:
            argv+=['--cover-package=%s' % self.package_name, '--with-coverage',
                   '--cover-tests', '--cover-erase']
        # construct list of plugins
        import nose.plugins.builtin
        from .noseclasses import KnownFailure, Unplugger
        plugins = [KnownFailure()]
        plugins += [p() for p in nose.plugins.builtin.plugins]
        # add doctesting if required
        doctest_argv = '--with-doctest' in argv
        if doctests == False and doctest_argv:
            doctests = True
        plug = self._get_custom_doctester()
        if plug is None:
            # use standard doctesting
            if doctests and not doctest_argv:
                argv += ['--with-doctest']
        else: # custom doctesting
            if doctest_argv: # in fact the unplugger would take care of this
                argv.remove('--with-doctest')
            plugins += [Unplugger('doctest'), plug]
            if doctests:
                argv += ['--with-' + plug.name]
        return argv, plugins

    def test(self, label='fast', verbose=1, extra_argv=None,
            doctests=False, coverage=False,
            raise_warnings=None):
        """
        Run tests for module using nose.

        Parameters
        ----------
        label : {'fast', 'full', '', attribute identifier}, optional
            Identifies the tests to run. This can be a string to pass to
            the nosetests executable with the '-A' option, or one of several
            special values.  Special values are:
            * 'fast' - the default - which corresponds to the ``nosetests -A``
              option of 'not slow'.
            * 'full' - fast (as above) and slow tests as in the
              'no -A' option to nosetests - this is the same as ''.
            * None or '' - run all tests.
            attribute_identifier - string passed directly to nosetests as '-A'.
        verbose : int, optional
            Verbosity value for test outputs, in the range 1-10. Default is 1.
        extra_argv : list, optional
            List with any extra arguments to pass to nosetests.
        doctests : bool, optional
            If True, run doctests in module. Default is False.
        coverage : bool, optional
            If True, report coverage of NumPy code. Default is False.
            (This requires the `coverage module:
             <http://nedbatchelder.com/code/modules/coverage.html>`_).
        raise_warnings : str or sequence of warnings, optional
            This specifies which warnings to configure as 'raise' instead
            of 'warn' during the test execution.  Valid strings are:

              - "develop" : equals ``(DeprecationWarning, RuntimeWarning)``
              - "release" : equals ``()``, don't raise on any warnings.

        Returns
        -------
        result : object
            Returns the result of running the tests as a
            ``nose.result.TextTestResult`` object.

        Notes
        -----
        Each NumPy module exposes `test` in its namespace to run all tests for it.
        For example, to run all tests for numpy.lib:

        >>> np.lib.test() #doctest: +SKIP

        Examples
        --------
        >>> result = np.lib.test() #doctest: +SKIP
        Running unit tests for numpy.lib
        ...
        Ran 976 tests in 3.933s

        OK

        >>> result.errors #doctest: +SKIP
        []
        >>> result.knownfail #doctest: +SKIP
        []
        """

        # cap verbosity at 3 because nose becomes *very* verbose beyond that
        verbose = min(verbose, 3)

        from . import utils
        utils.verbose = verbose

        if doctests:
            print("Running unit tests and doctests for %s" % self.package_name)
        else:
            print("Running unit tests for %s" % self.package_name)

        self._show_system_info()

        # reset doctest state on every run
        import doctest
        doctest.master = None

        if raise_warnings is None:
            raise_warnings = self.raise_warnings

        _warn_opts = dict(develop=(DeprecationWarning, RuntimeWarning),
                          release=())
        if raise_warnings in _warn_opts.keys():
            raise_warnings = _warn_opts[raise_warnings]

        with warnings.catch_warnings():
            # Reset the warning filters to the default state,
            # so that running the tests is more repeatable.
            warnings.resetwarnings()
            # If deprecation warnings are not set to 'error' below,
            # at least set them to 'warn'.
            warnings.filterwarnings('always', category=DeprecationWarning)
            # Force the requested warnings to raise
            for warningtype in raise_warnings:
                warnings.filterwarnings('error', category=warningtype)
            # Filter out annoying import messages.
            warnings.filterwarnings('ignore', message='Not importing directory')
            warnings.filterwarnings("ignore", message="numpy.dtype size changed")
            warnings.filterwarnings("ignore", message="numpy.ufunc size changed")
            warnings.filterwarnings("ignore", category=ModuleDeprecationWarning)
            warnings.filterwarnings("ignore", category=FutureWarning)
            # Filter out boolean '-' deprecation messages. This allows
            # older versions of scipy to test without a flood of messages.
            warnings.filterwarnings("ignore", message=".*boolean negative.*")
            warnings.filterwarnings("ignore", message=".*boolean subtract.*")
            # Filter out some deprecation warnings inside nose 1.3.7 when run
            # on python 3.5b2. See
            #     https://github.com/nose-devs/nose/issues/929
            warnings.filterwarnings("ignore", message=".*getargspec.*",
                                    category=DeprecationWarning,
                                    module="nose\.")

            from .noseclasses import NumpyTestProgram

            argv, plugins = self.prepare_test_args(
                    label, verbose, extra_argv, doctests, coverage)
            t = NumpyTestProgram(argv=argv, exit=False, plugins=plugins)

        return t.result

    def bench(self, label='fast', verbose=1, extra_argv=None):
        """
        Run benchmarks for module using nose.

        Parameters
        ----------
        label : {'fast', 'full', '', attribute identifier}, optional
            Identifies the benchmarks to run. This can be a string to pass to
            the nosetests executable with the '-A' option, or one of several
            special values.  Special values are:
            * 'fast' - the default - which corresponds to the ``nosetests -A``
              option of 'not slow'.
            * 'full' - fast (as above) and slow benchmarks as in the
              'no -A' option to nosetests - this is the same as ''.
            * None or '' - run all tests.
            attribute_identifier - string passed directly to nosetests as '-A'.
        verbose : int, optional
            Verbosity value for benchmark outputs, in the range 1-10. Default is 1.
        extra_argv : list, optional
            List with any extra arguments to pass to nosetests.

        Returns
        -------
        success : bool
            Returns True if running the benchmarks works, False if an error
            occurred.

        Notes
        -----
        Benchmarks are like tests, but have names starting with "bench" instead
        of "test", and can be found under the "benchmarks" sub-directory of the
        module.

        Each NumPy module exposes `bench` in its namespace to run all benchmarks
        for it.

        Examples
        --------
        >>> success = np.lib.bench() #doctest: +SKIP
        Running benchmarks for numpy.lib
        ...
        using 562341 items:
        unique:
        0.11
        unique1d:
        0.11
        ratio: 1.0
        nUnique: 56230 == 56230
        ...
        OK

        >>> success #doctest: +SKIP
        True

        """

        print("Running benchmarks for %s" % self.package_name)
        self._show_system_info()

        argv = self._test_argv(label, verbose, extra_argv)
        argv += ['--match', r'(?:^|[\\b_\\.%s-])[Bb]ench' % os.sep]

        # import nose or make informative error
        nose = import_nose()

        # get plugin to disable doctests
        from .noseclasses import Unplugger
        add_plugins = [Unplugger('doctest')]

        return nose.run(argv=argv, addplugins=add_plugins)
<|endoftext|>


<commit_before>""" Weight functions"""

import numpy as np
from numpy import ma
from numpy import pi

from fluid.common.distance import distance

# defining some weight functions

# blackman
def _weight_blackman(r,l):
    """
    using values from wikipedia 'exact blackman'

    """
    w = 0.42 +  0.5*np.cos(2*pi*r/l) + 0.08*np.cos(4*pi*r/l) # fase lag-> sign change
    w[np.absolute(r)>l/2.]=0
    return w

# rectangular

# triangular
def _weight_triangular(r,l):
    """
    """
    w = ma.masked_all(r.shape)
    ind = np.abs(r)<l/2.
    w[ind] = 1-np.abs(2*r[ind]/l)
    return w

def _weight_hamming(r,l):
    """
    """
    w=0.54+0.46*np.cos(pi*r/l)
    w[np.absolute(r)>l]=0
    return w

# hamming 2D
def _weight_hamming_2D(x, y, l):
    """
    """
    r = (x**2+y**2)**0.5
    w = 0.54 + 0.46*np.cos(2*pi*r/l)
    w[r>l] = 0
    return w

# hann
def _weight_hann(r,l):
    """
    """
    w=0.5*(1+np.cos(pi*r/l))
    w[np.absolute(r)>l]=0
    return w

# hann 2D
def _weight_hann_2D(x,y,l):
    """
    """
    r=(x**2+y**2)**0.5
    w=0.5*(1+np.cos(pi*r/l))
    w[r>l]=0
    return w

# lanczos 2D
def _weight_lanczos_2D(x,y,l,cutoff):
    """ Working on
    """
    #c=cutoff
    r=(x**2+y**2)**0.5
    w=np.sinc(r/cutoff)*np.sinc(r/cutoff/l)
    w[r>3*l]=0

# ==== funcoes antigas para eliminar ======================

def _weight_triangular_2D(x,y,l):
    """
    """
    r=(x**2+y**2)**0.5
    w=(l-r)/l
    w[r>l]=0
    return w

# hamming

<commit_msg>Fixing triangular window. Making it zero outside.<commit_after>""" Weight functions"""

import numpy as np
from numpy import ma
from numpy import pi

#from fluid.common.distance import distance

# defining some weight functions

# blackman
def _weight_blackman(r,l):
    """
    using values from wikipedia 'exact blackman'

    """
    w = 0.42 +  0.5*np.cos(2*pi*r/l) + 0.08*np.cos(4*pi*r/l) # fase lag-> sign change
    w[np.absolute(r)>l/2.]=0
    return w

# rectangular

# triangular
def _weight_triangular(r,l):
    """
    """
    w = ma.masked_all(r.shape)
    ind = np.abs(r)<l/2.
    ind2 = np.abs(r)>l/2.
    w[ind] = 1-np.abs(2*r[ind]/l)
    w[ind2] = 0
    return w

def _weight_hamming(r,l):
    """
    """
    w=0.54+0.46*np.cos(pi*r/l)
    w[np.absolute(r)>l]=0
    return w

# hamming 2D
def _weight_hamming_2D(x, y, l):
    """
    """
    r = (x**2+y**2)**0.5
    w = 0.54 + 0.46*np.cos(2*pi*r/l)
    w[r>l] = 0
    return w

# hann
def _weight_hann(r,l):
    """
    """
    w=0.5*(1+np.cos(pi*r/l))
    w[np.absolute(r)>l]=0
    return w

# hann 2D
def _weight_hann_2D(x,y,l):
    """
    """
    r=(x**2+y**2)**0.5
    w=0.5*(1+np.cos(pi*r/l))
    w[r>l]=0
    return w

# lanczos 2D
def _weight_lanczos_2D(x,y,l,cutoff):
    """ Working on
    """
    #c=cutoff
    r=(x**2+y**2)**0.5
    w=np.sinc(r/cutoff)*np.sinc(r/cutoff/l)
    w[r>3*l]=0

# ==== funcoes antigas para eliminar ======================

def _weight_triangular_2D(x,y,l):
    """
    """
    r=(x**2+y**2)**0.5
    w=(l-r)/l
    w[r>l]=0
    return w

# hamming

<|endoftext|>


<commit_before>"""Support for parametric tests in unittest.

:Author: Fernando Perez

Purpose
=======

Briefly, the main class in this module allows you to easily and cleanly
(without the gross name-mangling hacks that are normally needed) to write
unittest TestCase classes that have parametrized tests.  That is, tests which
consist of multiple sub-tests that scan for example a parameter range, but
where you want each sub-test to:

* count as a separate test in the statistics.

* be run even if others in the group error out or fail.

The class offers a simple name-based convention to create such tests (see
simple example at the end), in one of two ways:

* Each sub-test in a group can be run fully independently, with the
  setUp/tearDown methods being called each time.

* The whole group can be run with setUp/tearDown being called only once for the
  group.  This lets you conveniently reuse state that may be very expensive to
  compute for multiple tests.  Be careful not to corrupt it!!!

Caveats
=======

This code relies on implementation details of the unittest module (some key
methods are heavily modified versions of those, after copying them in).  So it
may well break either if you make sophisticated use of the unittest APIs, or if
unittest itself changes in the future.  I have only tested this with Python
2.5.

"""
__docformat__ = "restructuredtext en"

import unittest

class ParametricTestCase(unittest.TestCase):
    """TestCase subclass with support for parametric tests.

    Subclasses of this class can implement test methods that return a list of
    tests and arguments to call those with, to do parametric testing (often
    also called 'data driven' testing."""

    #: Prefix for tests with independent state.  These methods will be run with
    #: a separate setUp/tearDown call for each test in the group.
    _indepParTestPrefix = 'testip'

    #: Prefix for tests with shared state.  These methods will be run with
    #: a single setUp/tearDown call for the whole group.  This is useful when
    #: writing a group of tests for which the setup is expensive and one wants
    #: to actually share that state.  Use with care (especially be careful not
    #: to mutate the state you are using, which will alter later tests).
    _shareParTestPrefix = 'testsp'

    def exec_test(self,test,args,result):
        """Execute a single test.  Returns a success boolean"""

        ok = False
        try:
            test(*args)
            ok = True
        except self.failureException:
            result.addFailure(self, self._exc_info())
        except KeyboardInterrupt:
            raise
        except:
            result.addError(self, self._exc_info())

        return ok

    def run_test(self, testInfo,result):
        """Run one test with arguments"""

        test,args = testInfo[0],testInfo[1:]

        # Reset the doc attribute to be the docstring of this particular test,
        # so that in error messages it prints the actual test's docstring and
        # not that of the test factory.
        self._testMethodDoc = test.__doc__
        result.startTest(self)
        try:
            try:
                self.setUp()
            except KeyboardInterrupt:
                raise
            except:
                result.addError(self, self._exc_info())
                return

            ok = self.exec_test(test,args,result)

            try:
                self.tearDown()
            except KeyboardInterrupt:
                raise
            except:
                result.addError(self, self._exc_info())
                ok = False
            if ok: result.addSuccess(self)
        finally:
            result.stopTest(self)

    def run_tests(self, tests,result):
        """Run many tests with a common setUp/tearDown.

        The entire set of tests is run with a single setUp/tearDown call."""

        try:
            self.setUp()
        except KeyboardInterrupt:
            raise
        except:
            result.testsRun += 1
            result.addError(self, self._exc_info())
            return

        saved_doc = self._testMethodDoc

        try:
            # Run all the tests specified
            for testInfo in tests:
                test,args = testInfo[0],testInfo[1:]

                # Set the doc argument for this test.  Note that even if we do
                # this, the fail/error tracebacks still print the docstring for
                # the parent factory, because they only generate the message at
                # the end of the run, AFTER we've restored it.  There is no way
                # to tell the unittest system (without overriding a lot of
                # stuff) to extract this information right away, the logic is
                # hardcoded to pull it later, since unittest assumes it doesn't
                # change.
                self._testMethodDoc = test.__doc__
                result.startTest(self)
                ok = self.exec_test(test,args,result)
                if ok: result.addSuccess(self)

        finally:
            # Restore docstring info and run tearDown once only.
            self._testMethodDoc = saved_doc
            try:
                self.tearDown()
            except KeyboardInterrupt:
                raise
            except:
                result.addError(self, self._exc_info())

    def run(self, result=None):
        """Test runner."""

        #print
        #print '*** run for method:',self._testMethodName  # dbg
        #print '***            doc:',self._testMethodDoc  # dbg

        if result is None: result = self.defaultTestResult()

        # Independent tests: each gets its own setup/teardown
        _testMethodName = getattr(self,"_testMethodName", "runTest")
        if _testMethodName.startswith(self._indepParTestPrefix):
            for t in getattr(self,_testMethodName)():
                self.run_test(t,result)
        # Shared-state test: single setup/teardown for all
        elif _testMethodName.startswith(self._shareParTestPrefix):
            tests = getattr(self,_testMethodName,'runTest')()
            self.run_tests(tests,result)
        # Normal unittest Test methods
        else:
            unittest.TestCase.run(self,result)

#############################################################################
# Quick and dirty interactive example/test
if __name__ == '__main__':

    class ExampleTestCase(ParametricTestCase):

        #-------------------------------------------------------------------
        # An instrumented setUp method so we can see when it gets called and
        # how many times per instance
        counter = 0

        def setUp(self):
            self.counter += 1
            print 'setUp count: %2s for: %s' % (self.counter,
                                                self._testMethodDoc)

        #-------------------------------------------------------------------
        # A standard test method, just like in the unittest docs.
        def test_foo(self):
            """Normal test for feature foo."""
            pass

        #-------------------------------------------------------------------
        # Testing methods that need parameters.  These can NOT be named test*,
        # since they would be picked up by unittest and called without
        # arguments.  Instead, call them anything else (I use tst*) and then
        # load them via the factories below.
        def tstX(self,i):
            "Test feature X with parameters."
            print 'tstX, i=',i
            if i==1 or i==3:
                # Test fails
                self.fail('i is bad, bad: %s' % i)

        def tstY(self,i):
            "Test feature Y with parameters."
            print 'tstY, i=',i
            if i==1:
                # Force an error
                1/0

        def tstXX(self,i,j):
            "Test feature XX with parameters."
            print 'tstXX, i=',i,'j=',j
            if i==1:
                # Test fails
                self.fail('i is bad, bad: %s' % i)

        def tstYY(self,i):
            "Test feature YY with parameters."
            print 'tstYY, i=',i
            if i==2:
                # Force an error
                1/0

        def tstZZ(self):
            """Test feature ZZ without parameters, needs multiple runs.

            This could be a random test that you want to run multiple times."""
            pass

        #-------------------------------------------------------------------
        # Parametric test factories that create the test groups to call the
        # above tst* methods with their required arguments.
        def testip(self):
            """Independent parametric test factory.

            A separate setUp() call is made for each test returned by this
            method.

            You must return an iterable (list or generator is fine) containing
            tuples with the actual method to be called as the first argument,
            and the arguments for that call later."""
            return ((self.tstX,i) for i in range(5))

        def testip2(self):
            """Another independent parametric test factory"""
            return ((self.tstY,i) for i in range(5))

        def testip3(self):
            """Test factory combining different subtests.

            This one shows how to assemble calls to different tests."""
            return [(self.tstX,3),(self.tstX,9),(self.tstXX,4,10),
                    (self.tstZZ,),(self.tstZZ,)]

        def testsp(self):
            """Shared parametric test factory

            A single setUp() call is made for all the tests returned by this
            method.
            """
            return ((self.tstXX,i,i+1) for i in range(5))

        def testsp2(self):
            """Another shared parametric test factory"""
            return ((self.tstYY,i) for i in range(5))

        def testsp3(self):
            """Another shared parametric test factory.

            This one simply calls the same test multiple times, without any
            arguments.  Note that you must still return tuples, even if there
            are no arguments."""
            return ((self.tstZZ,) for i in range(10))

    # This test class runs normally under unittest's default runner
    unittest.main()
<commit_msg>Fix parametric testing for Python 2.4.<commit_after>"""Support for parametric tests in unittest.

:Author: Fernando Perez

Purpose
=======

Briefly, the main class in this module allows you to easily and cleanly
(without the gross name-mangling hacks that are normally needed) to write
unittest TestCase classes that have parametrized tests.  That is, tests which
consist of multiple sub-tests that scan for example a parameter range, but
where you want each sub-test to:

* count as a separate test in the statistics.

* be run even if others in the group error out or fail.

The class offers a simple name-based convention to create such tests (see
simple example at the end), in one of two ways:

* Each sub-test in a group can be run fully independently, with the
  setUp/tearDown methods being called each time.

* The whole group can be run with setUp/tearDown being called only once for the
  group.  This lets you conveniently reuse state that may be very expensive to
  compute for multiple tests.  Be careful not to corrupt it!!!

Caveats
=======

This code relies on implementation details of the unittest module (some key
methods are heavily modified versions of those, after copying them in).  So it
may well break either if you make sophisticated use of the unittest APIs, or if
unittest itself changes in the future.  I have only tested this with Python
2.5.

"""
__docformat__ = "restructuredtext en"

import unittest

class ParametricTestCase(unittest.TestCase):
    """TestCase subclass with support for parametric tests.

    Subclasses of this class can implement test methods that return a list of
    tests and arguments to call those with, to do parametric testing (often
    also called 'data driven' testing."""

    #: Prefix for tests with independent state.  These methods will be run with
    #: a separate setUp/tearDown call for each test in the group.
    _indepParTestPrefix = 'testip'

    #: Prefix for tests with shared state.  These methods will be run with
    #: a single setUp/tearDown call for the whole group.  This is useful when
    #: writing a group of tests for which the setup is expensive and one wants
    #: to actually share that state.  Use with care (especially be careful not
    #: to mutate the state you are using, which will alter later tests).
    _shareParTestPrefix = 'testsp'

    def exec_test(self,test,args,result):
        """Execute a single test.  Returns a success boolean"""

        ok = False
        try:
            test(*args)
            ok = True
        except self.failureException:
            result.addFailure(self, self._exc_info())
        except KeyboardInterrupt:
            raise
        except:
            result.addError(self, self._exc_info())

        return ok

    def run_test(self, testInfo,result):
        """Run one test with arguments"""

        test,args = testInfo[0],testInfo[1:]

        # Reset the doc attribute to be the docstring of this particular test,
        # so that in error messages it prints the actual test's docstring and
        # not that of the test factory.
        self._testMethodDoc = test.__doc__
        result.startTest(self)
        try:
            try:
                self.setUp()
            except KeyboardInterrupt:
                raise
            except:
                result.addError(self, self._exc_info())
                return

            ok = self.exec_test(test,args,result)

            try:
                self.tearDown()
            except KeyboardInterrupt:
                raise
            except:
                result.addError(self, self._exc_info())
                ok = False
            if ok: result.addSuccess(self)
        finally:
            result.stopTest(self)

    def run_tests(self, tests,result):
        """Run many tests with a common setUp/tearDown.

        The entire set of tests is run with a single setUp/tearDown call."""

        try:
            self.setUp()
        except KeyboardInterrupt:
            raise
        except:
            result.testsRun += 1
            result.addError(self, self._exc_info())
            return

        saved_doc = self._testMethodDoc

        try:
            # Run all the tests specified
            for testInfo in tests:
                test,args = testInfo[0],testInfo[1:]

                # Set the doc argument for this test.  Note that even if we do
                # this, the fail/error tracebacks still print the docstring for
                # the parent factory, because they only generate the message at
                # the end of the run, AFTER we've restored it.  There is no way
                # to tell the unittest system (without overriding a lot of
                # stuff) to extract this information right away, the logic is
                # hardcoded to pull it later, since unittest assumes it doesn't
                # change.
                self._testMethodDoc = test.__doc__
                result.startTest(self)
                ok = self.exec_test(test,args,result)
                if ok: result.addSuccess(self)

        finally:
            # Restore docstring info and run tearDown once only.
            self._testMethodDoc = saved_doc
            try:
                self.tearDown()
            except KeyboardInterrupt:
                raise
            except:
                result.addError(self, self._exc_info())

    def run(self, result=None):
        """Test runner."""

        #print
        #print '*** run for method:',self._testMethodName  # dbg
        #print '***            doc:',self._testMethodDoc  # dbg

        if result is None: result = self.defaultTestResult()

        try:
            _testMethodName = getattr(self,"_testMethodName")
        except:
            _testMethodName = getattr(self,"_TestCase__testMethodName")

        # Independent tests: each gets its own setup/teardown
        if _testMethodName.startswith(self._indepParTestPrefix):
            for t in getattr(self,_testMethodName)():
                self.run_test(t,result)
        # Shared-state test: single setup/teardown for all
        elif _testMethodName.startswith(self._shareParTestPrefix):
            tests = getattr(self,_testMethodName,'runTest')()
            self.run_tests(tests,result)
        # Normal unittest Test methods
        else:
            unittest.TestCase.run(self,result)

#############################################################################
# Quick and dirty interactive example/test
if __name__ == '__main__':

    class ExampleTestCase(ParametricTestCase):

        #-------------------------------------------------------------------
        # An instrumented setUp method so we can see when it gets called and
        # how many times per instance
        counter = 0

        def setUp(self):
            self.counter += 1
            print 'setUp count: %2s for: %s' % (self.counter,
                                                self._testMethodDoc)

        #-------------------------------------------------------------------
        # A standard test method, just like in the unittest docs.
        def test_foo(self):
            """Normal test for feature foo."""
            pass

        #-------------------------------------------------------------------
        # Testing methods that need parameters.  These can NOT be named test*,
        # since they would be picked up by unittest and called without
        # arguments.  Instead, call them anything else (I use tst*) and then
        # load them via the factories below.
        def tstX(self,i):
            "Test feature X with parameters."
            print 'tstX, i=',i
            if i==1 or i==3:
                # Test fails
                self.fail('i is bad, bad: %s' % i)

        def tstY(self,i):
            "Test feature Y with parameters."
            print 'tstY, i=',i
            if i==1:
                # Force an error
                1/0

        def tstXX(self,i,j):
            "Test feature XX with parameters."
            print 'tstXX, i=',i,'j=',j
            if i==1:
                # Test fails
                self.fail('i is bad, bad: %s' % i)

        def tstYY(self,i):
            "Test feature YY with parameters."
            print 'tstYY, i=',i
            if i==2:
                # Force an error
                1/0

        def tstZZ(self):
            """Test feature ZZ without parameters, needs multiple runs.

            This could be a random test that you want to run multiple times."""
            pass

        #-------------------------------------------------------------------
        # Parametric test factories that create the test groups to call the
        # above tst* methods with their required arguments.
        def testip(self):
            """Independent parametric test factory.

            A separate setUp() call is made for each test returned by this
            method.

            You must return an iterable (list or generator is fine) containing
            tuples with the actual method to be called as the first argument,
            and the arguments for that call later."""
            return ((self.tstX,i) for i in range(5))

        def testip2(self):
            """Another independent parametric test factory"""
            return ((self.tstY,i) for i in range(5))

        def testip3(self):
            """Test factory combining different subtests.

            This one shows how to assemble calls to different tests."""
            return [(self.tstX,3),(self.tstX,9),(self.tstXX,4,10),
                    (self.tstZZ,),(self.tstZZ,)]

        def testsp(self):
            """Shared parametric test factory

            A single setUp() call is made for all the tests returned by this
            method.
            """
            return ((self.tstXX,i,i+1) for i in range(5))

        def testsp2(self):
            """Another shared parametric test factory"""
            return ((self.tstYY,i) for i in range(5))

        def testsp3(self):
            """Another shared parametric test factory.

            This one simply calls the same test multiple times, without any
            arguments.  Note that you must still return tuples, even if there
            are no arguments."""
            return ((self.tstZZ,) for i in range(10))

    # This test class runs normally under unittest's default runner
    unittest.main()
<|endoftext|>


<commit_before># ABC Parser for ABC Music Notation Files

from __future__ import division

import re
import string
import math
from Preprocess import globalConstant

class TuneBook(object):
	"""
	Represents a tunebook with tunes and free text.

	Properties
	----------
	text
		An array of free text blocks, as strings.
	tune
		An array of tunes, as Tune objects.
	"""

	def __init__(self, filename=None):
		"""
		Creates a TuneBook object. If a filename is given, the file is opened
		and parsed. If an invalid filename is given, throws IOError.
		"""

		self.text = []    	# array of text blocks as strings
		self.tune = []   	# array of tunes as Tune

		if filename:
			f = open(filename, 'Ur')
			self.parse(f.read())
			f.close()

	def parse(self, str):
		"""
		Parses the given input.
		"""
		for lines in str.split('\n\n'):
			if 'x:' in lines.lower():
				tune = Tune()
				tune.parse(lines)
				self.tune.append(tune)
			else:
				self.text.append(lines)

##############################################################################
class Tune(object):
	"""
	Represents an entire tune with information fields and music.

	Properties
	----------
	text
		An array of the lines of the tune, as strings.
	line
		An array of the lines of the tune, as Line objects (see below).
	"""

	def __init__(self, filename=None):
		"""
		Creates a Tune object. If a filename is given, the file is opened and
		parsed.If an invalid filename is given, throws IOError.
		"""

		self._fields = {}	# information fields
		self.text = []    	# array of tune lines as strings
		self.line = []   	# array of tune lines as Line

		if filename:
			f = open(filename, 'Ur')
			self.parse(f.read())
			f.close()

	def field(self, field):
		"""
		Returns an information field (e.g., "T", "X"), or None if the given field
		doesn't exist.
		"""
		if field in self._fields:
			return self._fields[field]
		else:
			return None

	def parse(self, str):
		"""
		Parses the given input ABC string.
		"""
		lineBuffer = ''
		lines = str.split('\n')

		for line in lines:

			# Strip superfluous characters.
			line = re.sub('%.*$', '', line) # Strip comments.
			line = line.lstrip().rstrip()   # Strip whitespace.

			# Ignore blank lines.
			if len(line) == 0:
				continue

			# If the lines begins with a letter and a colon, it's an information
			# field. Extract it.
			matches = re.match('([A-Za-z]):\s*(.*)', line)
			if matches:
				#(0) matches the whole regular expression.
				#(1) matches the first pattern.
				#(2) matches the second pattern,etc.
				self._parseInformationField(matches.group(1), matches.group(2))
			else:
				# We have a tune line.
				if line[-1] == "\\":
					# The current line ends with a \, so just store it in the buffer
					# for now.
					lineBuffer += line.rstrip("\\")
				else:
					# The current line does not end with a \, so add it to whatever
					# lines we might have seen previously and parse it.
					lineBuffer += line
					self.text.append(lineBuffer) # Store raw tune line.
					self.line.append(Line(lineBuffer))
					lineBuffer = ''

	def _parseInformationField(self, field, data):
		# Parses an information field. field is a letter, while data is the
		# data following the field identifier. field is converted to uppercase
		# before storage. Only the first occurrence of the field is stored.
		field = field.upper()
		if field not in self._fields:
			self._fields[field] = data

	def getFields(self):
		return self._fields

##############################################################################
class Line(object):
	"""
	Represents one line in a tune.

	Properties
	----------
	text
		The raw text that was parsed.
	measure
		An array of Measure objects representing the individual measures
		within the line.
	"""

	def __init__(self, line=None):
		"""
		Takes a text line and parses it.
		"""
		self.text = None 	# raw text of the line
		self.measure = [] 	# array of Measures
		if line:
			self.parse(line)

	def parse(self, line):
		"""
		Parses a line of ABC.
		"""
		self.__init__()
		self.text = line

		# Split the line into measures. Measure symbols are
		# |, |], ||, [|, |:, :|, ::
		measures = re.split('\||\|]|\|\||\[\||\|:|:\||::', line)

		# Remove empty measures (typically at the end of lines).
		for item in measures:
			if len(item.lstrip().rstrip()) == 0:
				measures.remove(item)

		self.measure = [] # array of Measure objects
		for measure in measures:
			newMeasure = Measure()
			newMeasure.parse(measure)
			self.measure.append(newMeasure)

	def __str__(self):
		return self.text

##############################################################################
class Measure(object):
	"""
	Represents one measure of a line of music.

	Properties
	----------
	text
		The raw text of the measure that was parsed.
	item
		An array of MusicItem objects representing the individual items (notes and
		chords) within this measure.
	repeat
		The repeat number for this measure, or None if there is no repeat.
		This only simply repeats, e.g., [1 and [2
	"""

	def __init__(self):
		"""
		Constructor. Builds an empty Measure object.
		"""
		self._reset()

	def parse(self, text):
		"""
		Parses a string of ABC into Notes and Chords.
		"""
		self._reset()
		self.text = text

		match = re.search('\[([12])', self.text)
		if match:
			# First or second repeat.
			self.repeat = int(match.group(1))
			self._pos += len(match.group(0))

		while self._pos < len(self.text):

			if self.text[self._pos].isspace():
				# Ignore whitespace.
				self._pos += 1

			elif self.text[self._pos] == '"':
				# Parse a chord.
				self._parseChord()

			elif self.text[self._pos] in "^=_" or self.text[self._pos].isalpha() or self.text[self._pos] == '#':
				# Found the start of a note.
				self._parseNote()

			else:
				# Skip over anything we don't recognize.
				self._pos += 1

	def _parseChord(self):
		# Parses a chord.
		newChord = Chord()
		chordText = newChord.parse(self.text[self._pos:])
		newChord.beat = self._beat
		self._beat += newChord.duration
		self.item.append(newChord)
		self._pos += len(chordText) + 2 # add 2 to account for the double quotes

	def _parseNote(self):
		# Parses a note.
		newNote = Note()
		noteText, temp1, temp2, temp3 = newNote.parse(self.text[self._pos:])
		newNote.beat = self._beat
		self._beat += newNote.duration
		self.item.append(newNote)
		self._pos += len(noteText)

	def _reset(self):
		# Clears out all data.
		self.item = []		# array of Chords and Notes for this measure
		self.text = None	# raw text of the measure
		self._pos = 0 		# parsing position within the measure
		self.repeat = None	# repeat number (1 or 2)
		self._beat = 1		# current beat (while parsing)

	def __str__(self):
		return self.text

##############################################################################
class MusicItem(object):
	"""
	Abstract base class for "things" that appear in a line of music:
	notes and chords.

	Properties
	----------
	duration
		Length of this item as a float, e.g., 0.25, 1, etc.
	beat
		The beat on which this item occurs (float). Starts at 1.
	text
		The raw text of this item.
	"""

	def __init__(self):
		# Duration of the item as a float, e.g,. 1/4, 1/8, 1/16, 2
		self.duration = 0.0

		# The beat on which this item occurs: 0, 1, 2, etc.
		self.beat = 0.0

		# Raw text from the tune that makes up this item.
		self.text = ''

	def __str__(self):
		return self.text

##############################################################################
class Chord(MusicItem):
	"""
	Represents a chord.
	"""
	def __init__(self):
		super(Chord, self).__init__()

	def parse(self, str):
		"""
		Parses a chord out of the given string. Returns the raw text that
		was parsed from str without the surrounding	double quotes.
		"""
		pos = 0
		if pos < len(str) and str[pos] == '"':
			self.text += str[pos]
			pos += 1
		else:
			raise RuntimeError('Chord does not begin with ".' + str)

		while pos < len(str) and str[pos] != '"':
			self.text += str[pos]
			pos += 1

		if pos < len(str) and str[pos] == '"':
			self.text += str[pos]
			pos += 1
		else:
			raise RuntimeError('Chord does not end with ":' + str)

		# Remove surrounding double quotes.
		self.text = self.text[1:-1]
		return self.text

##############################################################################
#get duration information
class Note(MusicItem):
	"""
	Represents a note.

	Properties
	----------
	prefix
		Optional ^, =, or _
	note
		The note character itself, A, B, etc.
	suffix
		Optional ' or ,
	length
		Optional note length, /4, 2, etc.
	"""
	def __init__(self):
		super(Note, self).__init__()
		self.prefix = None	# optional ^, =, or _
		self.note = None	# note character [A-z]
		self.suffix = None	# optional ' or ,
		self.length = None	# optional length indication
		self.nextNoteDurationPlus = 0.0 # the value that the next note take away, when the current note has < or >
		self.nextNoteDurationFlag = False # whether the next note takes away the value or not

	def parse(self, str, nextNoteDurationPlus = 0.0, nextNoteDurationFlag = False):
		"""
		Parses a note out of the given string. Returns the raw text that
		was parsed from str.
		"""
		self.__init__()
		pos = 0

		if str == '#ending':
			self.text = '#ending'
			self.duration = 0
			self.nextNoteDurationPlus = 0.0
			self.nextNoteDurationFlag = False
			return self.text, self.duration , self.nextNoteDurationPlus, self.nextNoteDurationFlag

		if pos < len(str) and str[pos] in "^=_":
			# Sharp, natural, or flat symbol.
			self.text += str[pos]
			self.prefix = str[pos]
			pos += 1

		if pos < len(str) and str[pos].isalpha():
			# Note letter.
			self.text += str[pos]
			self.note = str[pos]
			pos += 1
		else:
			raise RuntimeError('Note does not contain a character: ' + str.__str__())

		if pos < len(str) and str[pos] in "',":
			# Note raise or lower an octave.
			self.text += str[pos]
			self.suffix = str[pos]
			pos += 1

		while pos < len(str) and str[pos] in "/0123456789><":
			# Note length.
			self.text += str[pos]
			if not self.length:
				self.length = ""
			self.length += str[pos]
			pos += 1

		#turn the note length(string) into a duration(float).
		#given that all data is valid

		slash_count = self.length.__str__().count('/')
		#this dotted-note notation is only defined between two notes of equal length.
		#attention: two notes which are of equal length
		left_count = self.length.__str__().count('<')
		right_count = self.length.__str__().count('>')

		self.nextNoteDurationFlag = nextNoteDurationFlag
		self.nextNoteDurationPlus = nextNoteDurationPlus

		#print(self.length)
		#if it is just a sigle note
		if self.length is None:
			#if the previous note has < or > suffix
			if self.nextNoteDurationFlag == True:
				self.duration = globalConstant.nextNoteDurationBase + self.nextNoteDurationPlus
				#print(self.duration)

			#if it does not have
			else:
				self.duration = globalConstant.nextNoteDurationBase
				#print(self.duration)
			self.nextNoteDurationPlus = 0.0
			self.nextNoteDurationFlag = False
		#if it is a sigle note followed by a number
		elif slash_count ==0 and left_count ==0 and right_count ==0:
			#and if the previous note have < or >
			if self.nextNoteDurationFlag:
				self.duration = float(re.match('[0-9]', self.length).group(0)) + self.nextNoteDurationPlus
			#or it does not have < and >
			else:
				self.duration = float(re.match('[0-9]', self.length).group(0))
			self.nextNoteDurationPlus = 0.0
			self.nextNoteDurationFlag = False

		else:
			#if it has a /
			if slash_count == 1:
				#if it has only a /, without any number
				if re.search('[0-9]', self.length) == None:
					#if the previous note has < or >
					if self.nextNoteDurationFlag == True:
						self.duration = 1/2 + self.nextNoteDurationPlus
					else:
						self.duration = 1/2

				#or if it has a / with numbers
				else:
					nums = re.findall('[0-9]', self.length)
					#if it has two number
					if len(nums) == 2:
						#if the previous note has < or >
						if self.nextNoteDurationFlag == True:
							self.duration = eval(re.match('[0-9]/[0-9]', self.length).group(0)) + self.nextNoteDurationPlus
						else:
							self.duration = eval(re.match('[0-9]/[0-9]', self.length).group(0))

					#if it has only one number
					elif len(nums) == 1:
						#if the case is like /3, it means 1/3
						if re.search('[0-9]/', self.length) == None:
							#if the previous note has < or >
							if self.nextNoteDurationFlag == True:
								#self.duration = eval('1/' + re.search('/[0-9]', self.length).group(0)) + _nextNoteDurationPlus
								self.duration = eval('1/' + nums[0]) + self.nextNoteDurationPlus
							#if it does not have < and >
							else:
								#self.duration = eval('1' + re.search('/[0-9]', self.length).group(0))
								self.duration = eval('1/' + nums[0])

						##if the case is like 3/, it means 3/2
						else:
							if self.nextNoteDurationFlag == True:
								self.duration = eval(nums[0] + '/2') + self.nextNoteDurationPlus
							else:
								self.duration = eval(nums[0] + '/2')

			#if it has more than one /
			elif slash_count > 1:
				if self.nextNoteDurationFlag == True:
					self.duration = globalConstant.nextNoteDurationBase / math.pow(2, slash_count) + self.nextNoteDurationPlus
				else:
					self.duration = globalConstant.nextNoteDurationBase / math.pow(2, slash_count)

			#if it has no /
			else:
				# if it has also no number
				if re.search('[0-9]', self.length) == None:
					#if the previous note has < or >
					if self.nextNoteDurationFlag == True:
						self.duration = globalConstant.nextNoteDurationBase +self.nextNoteDurationPlus
						#print(self.duration)
					#if the previous note does not have
					else:
						self.duration = globalConstant.nextNoteDurationBase

				#or if also have one number
				else:
					if self.nextNoteDurationFlag == True:
						self.duration = float(re.search('[0-9]', self.length).group(0)) + self.nextNoteDurationPlus
					# if the previous note does not have < and >
					else:
						self.duration = float(re.search('[0-9]', self.length).group(0))

			#if it also has <
			if left_count != 0:
				takeaway_part = self.duration / math.pow(2, left_count)
				self.duration =  takeaway_part
				self.nextNoteDurationFlag = True
				self.nextNoteDurationPlus = takeaway_part

			#or if it also has >
			elif right_count != 0:
				takeaway_part = self.duration / math.pow(2, right_count)
				self.duration = self.duration + takeaway_part
				self.nextNoteDurationFlag = True
				self.nextNoteDurationPlus = -(takeaway_part)

			# if it has no < and >
			else:
				self.nextNoteDurationFlag = False
				self.nextNoteDurationPlus = 0.0

		return self.text, self.duration , self.nextNoteDurationPlus, self.nextNoteDurationFlag
<commit_msg>Update the Preprocessing part<commit_after><|endoftext|>


<commit_before>from __future__ import unicode_literals

import logging
import six
import time

from twisted.python.compat import _PY3
from twisted.web import http
from twisted.protocols.policies import ProtocolWrapper

from .ws_protocol import WebSocketProtocol, WebSocketFactory

logger = logging.getLogger(__name__)

class WebRequest(http.Request):
    """
    Request that either hands off information to channels, or offloads
    to a WebSocket class.

    Does some extra processing over the normal Twisted Web request to separate
    GET and POST out.
    """

    def __init__(self, *args, **kwargs):
        http.Request.__init__(self, *args, **kwargs)
        # Easy factory link
        self.factory = self.channel.factory
        # Make a name for our reply channel
        self.reply_channel = self.factory.channel_layer.new_channel("!http.response.?")
        # Tell factory we're that channel's client
        self.last_keepalive = time.time()
        self.factory.reply_protocols[self.reply_channel] = self
        self._got_response_start = False

    def process(self):
        self.request_start = time.time()
        # Get upgrade header
        upgrade_header = None
        if self.requestHeaders.hasHeader(b"Upgrade"):
            upgrade_header = self.requestHeaders.getRawHeaders(b"Upgrade")[0]
        # Calculate query string
        self.query_string = b""
        if b"?" in self.uri:
            self.query_string = self.uri.split(b"?", 1)[1]
        # Is it WebSocket? IS IT?!
        if upgrade_header == b"websocket":
            # Make WebSocket protocol to hand off to
            protocol = self.factory.ws_factory.buildProtocol(self.transport.getPeer())
            if not protocol:
                # If protocol creation fails, we signal "internal server error"
                self.setResponseCode(500)
                logger.warn("Could not make WebSocket protocol")
                self.finish()
            # Port across transport
            protocol.set_main_factory(self.factory)
            transport, self.transport = self.transport, None
            if isinstance(transport, ProtocolWrapper):
                # i.e. TLS is a wrapping protocol
                transport.wrappedProtocol = protocol
            else:
                transport.protocol = protocol
            protocol.makeConnection(transport)
            # Re-inject request
            data = self.method + b' ' + self.uri + b' HTTP/1.1\x0d\x0a'
            for h in self.requestHeaders.getAllRawHeaders():
                data += h[0] + b': ' + b",".join(h[1]) + b'\x0d\x0a'
            data += b"\x0d\x0a"
            data += self.content.read()
            protocol.dataReceived(data)
            # Remove our HTTP reply channel association
            logger.debug("Upgraded connection %s to WebSocket %s", self.reply_channel, protocol.reply_channel)
            del self.factory.reply_protocols[self.reply_channel]
            self.reply_channel = None
        # Boring old HTTP.
        else:
            # Sanitize and decode headers
            self.clean_headers = {}
            for name, value in self.requestHeaders.getAllRawHeaders():
                # Prevent CVE-2015-0219
                if b"_" in name:
                    continue
                self.clean_headers[name.lower().decode("latin1")] = value[0]
            logger.debug("HTTP %s request for %s", self.method, self.reply_channel)
            self.content.seek(0, 0)
            # Send message
            self.factory.channel_layer.send("http.request", {
                "reply_channel": self.reply_channel,
                # TODO: Correctly say if it's 1.1 or 1.0
                "http_version": "1.1",
                "method": self.method.decode("ascii"),
                "path": self.path,
                "scheme": "http",
                "query_string": self.query_string,
                "headers": self.clean_headers,
                "body": self.content.read(),
                "client": [self.client.host, self.client.port],
                "server": [self.host.host, self.host.port],
            })

    def send_disconnect(self):
        """
        Sends a disconnect message on the http.disconnect channel.
        Useful only really for long-polling.
        """
        self.factory.channel_layer.send("http.disconnect", {
            "reply_channel": self.reply_channel,
        })

    def connectionLost(self, reason):
        """
        Cleans up reply channel on close.
        """
        if self.reply_channel and self.reply_channel in self.channel.factory.reply_protocols:
            self.send_disconnect()
            del self.channel.factory.reply_protocols[self.reply_channel]
        logger.debug("HTTP disconnect for %s", self.reply_channel)
        http.Request.connectionLost(self, reason)

    def finish(self):
        """
        Cleans up reply channel on close.
        """
        if self.reply_channel and self.reply_channel in self.channel.factory.reply_protocols:
            self.send_disconnect()
            del self.channel.factory.reply_protocols[self.reply_channel]
        logger.debug("HTTP close for %s", self.reply_channel)
        http.Request.finish(self)

    def serverResponse(self, message):
        """
        Writes a received HTTP response back out to the transport.
        """
        if "status" in message:
            if self._got_response_start:
                raise ValueError("Got multiple Response messages!")
            self._got_response_start = True
            # Write code
            status_text = message.get("status_text", None)
            if isinstance(status_text, six.text_type):
                logger.warn("HTTP status text for %s was text - should be bytes", self.reply_channel)
                status_text = status_text.encode("ascii")
            self.setResponseCode(message['status'], status_text)
            # Write headers
            for header, value in message.get("headers", {}):
                self.setHeader(header.encode("utf8"), value)
            logger.debug("HTTP %s response started for %s", message['status'], self.reply_channel)
        # Write out body
        if "content" in message:
            http.Request.write(self, message['content'])
        # End if there's no more content
        if not message.get("more_content", False):
            self.finish()
            logger.debug("HTTP response complete for %s", self.reply_channel)
            self.factory.log_action("http", "complete", {
                "path": self.path.decode("ascii"),
                "status": self.code,
                "method": self.method.decode("ascii"),
                "client": "%s:%s" % (self.client.host, self.client.port),
                "time_taken": time.time() - self.request_start,
            })
        else:
            logger.debug("HTTP response chunk for %s", self.reply_channel)

class HTTPProtocol(http.HTTPChannel):

    requestFactory = WebRequest

class HTTPFactory(http.HTTPFactory):
    """
    Factory which takes care of tracking which protocol
    instances or request instances are responsible for which
    named response channels, so incoming messages can be
    routed appropriately.
    """

    protocol = HTTPProtocol

    def __init__(self, channel_layer, action_logger=None):
        http.HTTPFactory.__init__(self)
        self.channel_layer = channel_layer
        self.action_logger = action_logger
        # We track all sub-protocols for response channel mapping
        self.reply_protocols = {}
        # Make a factory for WebSocket protocols
        self.ws_factory = WebSocketFactory(self)
        self.ws_factory.protocol = WebSocketProtocol
        self.ws_factory.reply_protocols = self.reply_protocols

    def reply_channels(self):
        return self.reply_protocols.keys()

    def dispatch_reply(self, channel, message):
        if channel.startswith("!http") and isinstance(self.reply_protocols[channel], WebRequest):
            self.reply_protocols[channel].serverResponse(message)
        elif channel.startswith("!websocket") and isinstance(self.reply_protocols[channel], WebSocketProtocol):
            if message.get("bytes", None):
                self.reply_protocols[channel].serverSend(message["bytes"], True)
            if message.get("text", None):
                self.reply_protocols[channel].serverSend(message["text"], False)
            if message.get("close", False):
                self.reply_protocols[channel].serverClose()
        else:
            raise ValueError("Cannot dispatch message on channel %r" % channel)

    def log_action(self, protocol, action, details):
        """
        Dispatches to any registered action logger, if there is one.
        """
        if self.action_logger:
            self.action_logger(protocol, action, details)
<commit_msg>Make WebSockt upgrade header case insensitive<commit_after>from __future__ import unicode_literals

import logging
import six
import time

from twisted.python.compat import _PY3
from twisted.web import http
from twisted.protocols.policies import ProtocolWrapper

from .ws_protocol import WebSocketProtocol, WebSocketFactory

logger = logging.getLogger(__name__)

class WebRequest(http.Request):
    """
    Request that either hands off information to channels, or offloads
    to a WebSocket class.

    Does some extra processing over the normal Twisted Web request to separate
    GET and POST out.
    """

    def __init__(self, *args, **kwargs):
        http.Request.__init__(self, *args, **kwargs)
        # Easy factory link
        self.factory = self.channel.factory
        # Make a name for our reply channel
        self.reply_channel = self.factory.channel_layer.new_channel("!http.response.?")
        # Tell factory we're that channel's client
        self.last_keepalive = time.time()
        self.factory.reply_protocols[self.reply_channel] = self
        self._got_response_start = False

    def process(self):
        self.request_start = time.time()
        # Get upgrade header
        upgrade_header = None
        if self.requestHeaders.hasHeader(b"Upgrade"):
            upgrade_header = self.requestHeaders.getRawHeaders(b"Upgrade")[0]
        # Calculate query string
        self.query_string = b""
        if b"?" in self.uri:
            self.query_string = self.uri.split(b"?", 1)[1]
        # Is it WebSocket? IS IT?!
        if upgrade_header.lower() == b"websocket":
            # Make WebSocket protocol to hand off to
            protocol = self.factory.ws_factory.buildProtocol(self.transport.getPeer())
            if not protocol:
                # If protocol creation fails, we signal "internal server error"
                self.setResponseCode(500)
                logger.warn("Could not make WebSocket protocol")
                self.finish()
            # Port across transport
            protocol.set_main_factory(self.factory)
            transport, self.transport = self.transport, None
            if isinstance(transport, ProtocolWrapper):
                # i.e. TLS is a wrapping protocol
                transport.wrappedProtocol = protocol
            else:
                transport.protocol = protocol
            protocol.makeConnection(transport)
            # Re-inject request
            data = self.method + b' ' + self.uri + b' HTTP/1.1\x0d\x0a'
            for h in self.requestHeaders.getAllRawHeaders():
                data += h[0] + b': ' + b",".join(h[1]) + b'\x0d\x0a'
            data += b"\x0d\x0a"
            data += self.content.read()
            protocol.dataReceived(data)
            # Remove our HTTP reply channel association
            logger.debug("Upgraded connection %s to WebSocket %s", self.reply_channel, protocol.reply_channel)
            del self.factory.reply_protocols[self.reply_channel]
            self.reply_channel = None
        # Boring old HTTP.
        else:
            # Sanitize and decode headers
            self.clean_headers = {}
            for name, value in self.requestHeaders.getAllRawHeaders():
                # Prevent CVE-2015-0219
                if b"_" in name:
                    continue
                self.clean_headers[name.lower().decode("latin1")] = value[0]
            logger.debug("HTTP %s request for %s", self.method, self.reply_channel)
            self.content.seek(0, 0)
            # Send message
            self.factory.channel_layer.send("http.request", {
                "reply_channel": self.reply_channel,
                # TODO: Correctly say if it's 1.1 or 1.0
                "http_version": "1.1",
                "method": self.method.decode("ascii"),
                "path": self.path,
                "scheme": "http",
                "query_string": self.query_string,
                "headers": self.clean_headers,
                "body": self.content.read(),
                "client": [self.client.host, self.client.port],
                "server": [self.host.host, self.host.port],
            })

    def send_disconnect(self):
        """
        Sends a disconnect message on the http.disconnect channel.
        Useful only really for long-polling.
        """
        self.factory.channel_layer.send("http.disconnect", {
            "reply_channel": self.reply_channel,
        })

    def connectionLost(self, reason):
        """
        Cleans up reply channel on close.
        """
        if self.reply_channel and self.reply_channel in self.channel.factory.reply_protocols:
            self.send_disconnect()
            del self.channel.factory.reply_protocols[self.reply_channel]
        logger.debug("HTTP disconnect for %s", self.reply_channel)
        http.Request.connectionLost(self, reason)

    def finish(self):
        """
        Cleans up reply channel on close.
        """
        if self.reply_channel and self.reply_channel in self.channel.factory.reply_protocols:
            self.send_disconnect()
            del self.channel.factory.reply_protocols[self.reply_channel]
        logger.debug("HTTP close for %s", self.reply_channel)
        http.Request.finish(self)

    def serverResponse(self, message):
        """
        Writes a received HTTP response back out to the transport.
        """
        if "status" in message:
            if self._got_response_start:
                raise ValueError("Got multiple Response messages!")
            self._got_response_start = True
            # Write code
            status_text = message.get("status_text", None)
            if isinstance(status_text, six.text_type):
                logger.warn("HTTP status text for %s was text - should be bytes", self.reply_channel)
                status_text = status_text.encode("ascii")
            self.setResponseCode(message['status'], status_text)
            # Write headers
            for header, value in message.get("headers", {}):
                self.setHeader(header.encode("utf8"), value)
            logger.debug("HTTP %s response started for %s", message['status'], self.reply_channel)
        # Write out body
        if "content" in message:
            http.Request.write(self, message['content'])
        # End if there's no more content
        if not message.get("more_content", False):
            self.finish()
            logger.debug("HTTP response complete for %s", self.reply_channel)
            self.factory.log_action("http", "complete", {
                "path": self.path.decode("ascii"),
                "status": self.code,
                "method": self.method.decode("ascii"),
                "client": "%s:%s" % (self.client.host, self.client.port),
                "time_taken": time.time() - self.request_start,
            })
        else:
            logger.debug("HTTP response chunk for %s", self.reply_channel)

class HTTPProtocol(http.HTTPChannel):

    requestFactory = WebRequest

class HTTPFactory(http.HTTPFactory):
    """
    Factory which takes care of tracking which protocol
    instances or request instances are responsible for which
    named response channels, so incoming messages can be
    routed appropriately.
    """

    protocol = HTTPProtocol

    def __init__(self, channel_layer, action_logger=None):
        http.HTTPFactory.__init__(self)
        self.channel_layer = channel_layer
        self.action_logger = action_logger
        # We track all sub-protocols for response channel mapping
        self.reply_protocols = {}
        # Make a factory for WebSocket protocols
        self.ws_factory = WebSocketFactory(self)
        self.ws_factory.protocol = WebSocketProtocol
        self.ws_factory.reply_protocols = self.reply_protocols

    def reply_channels(self):
        return self.reply_protocols.keys()

    def dispatch_reply(self, channel, message):
        if channel.startswith("!http") and isinstance(self.reply_protocols[channel], WebRequest):
            self.reply_protocols[channel].serverResponse(message)
        elif channel.startswith("!websocket") and isinstance(self.reply_protocols[channel], WebSocketProtocol):
            if message.get("bytes", None):
                self.reply_protocols[channel].serverSend(message["bytes"], True)
            if message.get("text", None):
                self.reply_protocols[channel].serverSend(message["text"], False)
            if message.get("close", False):
                self.reply_protocols[channel].serverClose()
        else:
            raise ValueError("Cannot dispatch message on channel %r" % channel)

    def log_action(self, protocol, action, details):
        """
        Dispatches to any registered action logger, if there is one.
        """
        if self.action_logger:
            self.action_logger(protocol, action, details)
<|endoftext|>