<commit_before>"""
Copyright (c) 2013-2014 Heiko Strathmann
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
 *
1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
 *
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the author.
"""

from numpy import arange, where
import numpy
from numpy.matlib import repmat
from numpy.random import rand, randint, permutation

from kameleon_mcmc.distribution.Distribution import Distribution, Sample

class DiscreteRandomWalkProposal(Distribution):
    def __init__(self, mu, spread):
        if not type(mu) is numpy.ndarray:
            raise TypeError("Mean vector must be a numpy array")

        if not len(mu.shape) == 1:
            raise ValueError("Mean vector must be a 1D numpy array")

        if not len(mu) > 0:
            raise ValueError("Mean vector dimension must be positive")

        if mu.dtype != numpy.bool8:
            raise ValueError("Mean must be a bool8 numpy array")

        Distribution.__init__(self, len(mu))

        if not type(spread) is float:
            raise TypeError("Spread must be a float")

        if not (spread > 0. and spread < 1.):
            raise ValueError("Spread must be a probability")

        self.mu = mu
        self.spread = spread

    def __str__(self):
        s = self.__class__.__name__ + "=["
        s += "spread=" + str(self.ps)
        s += ", " + Distribution.__str__(self)
        s += "]"
        return s

    def sample(self, n=1):
        if not type(n) is int:
            raise TypeError("Number of samples must be integer")

        if n <= 0:
            raise ValueError("Number of samples (%d) needs to be positive", n)

        # copy mean vector a couple of times
        samples = repmat(self.mu, n, 1)

        for n in range(n):
            # sample Bernoulli to get number of changes
            num_changes = sum(rand(self.dimension) < self.spread)

            # sample action (add=0,del=1,swap=2), has some advantages to represent
            # like this, see below
            action = randint(0, 3)

            # check that adding/deleting the desired number is possible,
            # truncate otherwise
            num_pos = sum(self.mu)
            if action is 0 and (num_changes + num_pos) > len(self.mu):
                num_changes = len(self.mu) - num_pos
            elif action is 1 and (num_changes > num_pos):
                num_changes = num_pos
            elif action is 2 and num_changes > min(num_pos, self.dimension - num_pos):
                num_changes = min(num_pos, self.dimension - num_pos)

            # if no changes, directly return
            if num_changes == 0:
                return Sample(samples)

            # do action, since we chacked the number of changes above, no checks here
            if action is 0 or action is 1:
                # do action: add/delete
                # add or delete is almost the same, below changes only the value in
                # the mean vector to look for
                value_to_change = (action == 1)
                relevant_indices = where(self.mu == value_to_change)[0]
                selected = permutation(arange(len(relevant_indices)))[:num_changes]
                changes = relevant_indices[selected]
                samples[n][changes] = 1
            elif action is 2:
                # do action: swap
                pos_indices = where(self.mu == 1)
                neg_indices = where(self.mu == 0)
                selected_pos = permutation(arange(len(pos_indices)))[:num_changes]
                selected_neg = permutation(arange(len(neg_indices)))[:num_changes]
                changes_pos = pos_indices[selected_pos]
                changes_neg = neg_indices[selected_neg]
                samples[n][changes_pos] = 0
                samples[n][changes_neg] = 1

        return Sample(samples)

    def log_pdf(self, X):
        if not type(X) is numpy.ndarray:
            raise TypeError("X must be a numpy array")

        if not len(X.shape) is 2:
            raise TypeError("X must be a 2D numpy array")

        # this also enforce correct data ranges
        if X.dtype != numpy.bool8:
            raise ValueError("X must be a bool8 numpy array")

        if not X.shape[1] == self.dimension:
            raise ValueError("Dimension of X does not match own dimension")

        return rand(len(X))

<commit_msg>implemented log_pdf and added comments from last meeting<commit_after>"""
Copyright (c) 2013-2014 Heiko Strathmann
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
 *
1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
 *
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the author.
"""

from numpy import arange, where, zeros, log
import numpy
from numpy.matlib import repmat
from numpy.random import rand, randint, permutation

from kameleon_mcmc.distribution.Distribution import Distribution, Sample
from kameleon_mcmc.tools.HelperFunctions import HelperFunctions

class DiscreteRandomWalkProposal(Distribution):
    def __init__(self, mu, spread):
        if not type(mu) is numpy.ndarray:
            raise TypeError("Mean vector must be a numpy array")

        if not len(mu.shape) == 1:
            raise ValueError("Mean vector must be a 1D numpy array")

        if not len(mu) > 0:
            raise ValueError("Mean vector dimension must be positive")

        if mu.dtype != numpy.bool8:
            raise ValueError("Mean must be a bool8 numpy array")

        Distribution.__init__(self, len(mu))

        if not type(spread) is float:
            raise TypeError("Spread must be a float")

        if not (spread > 0. and spread < 1.):
            raise ValueError("Spread must be a probability")

        self.mu = mu
        self.spread = spread

    def __str__(self):
        s = self.__class__.__name__ + "=["
        s += "spread=" + str(self.ps)
        s += ", " + Distribution.__str__(self)
        s += "]"
        return s

    def sample(self, n=1):
        if not type(n) is int:
            raise TypeError("Number of samples must be integer")

        if n <= 0:
            raise ValueError("Number of samples (%d) needs to be positive", n)

        # copy mean vector a couple of times
        samples = repmat(self.mu, n, 1)

        for i in range(n):
            num_active = sum(self.mu)

            # sample Bernoulli to get number of changes
            # N-1 Bernoulli trials, then add one afterwards
            max_possible_change = min(num_active, self.dimension - num_active)
            num_changes = sum(rand(max(max_possible_change - 1, 0)) < self.spread) + 1

            # sample action (add=0,del=1,swap=2), has some advantages to represent
            # like this, see below
            action = randint(0, 3)

            # check that adding/deleting the desired number is possible,
            # truncate otherwise, this is needed because if all elements are active
            # then adding is not possible, but we always have at least one change
            # this is a hack and we should email the authors about this
            if action is 0 and (num_changes + num_active) > self.dimension:
                num_changes = self.dimension - num_active
            elif action is 1 and (num_changes > num_active):
                num_changes = num_active
            elif action is 2 and num_changes > max_possible_change:
                num_changes = max_possible_change

            # if no changes, directly return
            if num_changes == 0:
                return Sample(samples)

            # do action, since we chacked the number of changes above, no checks here
            if action is 0 or action is 1:
                # do action: add/delete
                # add or delete is almost the same, below changes only the value in
                # the mean vector to look for
                value_to_change = (action == 1)
                relevant_indices = where(self.mu == value_to_change)[0]
                selected = permutation(arange(len(relevant_indices)))[:num_changes]
                changes = relevant_indices[selected]
                samples[i][changes] = 1
            elif action is 2:
                # do action: swap
                pos_indices = where(self.mu == 1)
                neg_indices = where(self.mu == 0)
                selected_pos = permutation(arange(len(pos_indices)))[:num_changes]
                selected_neg = permutation(arange(len(neg_indices)))[:num_changes]
                changes_pos = pos_indices[selected_pos]
                changes_neg = neg_indices[selected_neg]
                samples[i][changes_pos] = 0
                samples[i][changes_neg] = 1

        return Sample(samples)

    def log_pdf(self, X):
        if not type(X) is numpy.ndarray:
            raise TypeError("X must be a numpy array")

        if not len(X.shape) is 2:
            raise TypeError("X must be a 2D numpy array")

        # this also enforces correct data ranges
        if X.dtype != numpy.bool8:
            raise ValueError("X must be a bool8 numpy array")

        if not X.shape[1] == self.dimension:
            raise ValueError("Dimension of X does not match own dimension")

        num_active_self = sum(self.mu)
        num_active_X = sum(X, 0)
        num_diff = abs(num_active_self - num_active_X)
        max_possible_change = min(num_active_self, self.dimension - num_active_self)

        # result vector
        log_liks = zeros(len(X))

        # add shared term (same for all actions
        shared_term = -log(3.) \
                        + HelperFunctions.log_bin_coeff(max_possible_change - 1, num_diff - 1) \
                        + (num_diff - 1) * log(self.spread) \
                        + (max_possible_change - num_diff) * log(1 - self.spread)
        log_liks += shared_term

        # compute action dependent log likelihood parts
        for i in range(len(X)):
            # find out which action was in between current and given point
            if num_active_self == num_active_X[i]:
                # swap
                log_liks[i] -= HelperFunctions.log_bin_coeff(num_active_self, num_diff) \
                                - HelperFunctions.log_bin_coeff(self.dimension - num_active_self, num_diff)
            else:
                if num_active_self < num_active_X[i]:
                    # add
                    log_liks[i] -= HelperFunctions.log_bin_coeff(self.dimension - num_active_self, num_diff)
                else:
                    # del
                    log_liks[i] -= HelperFunctions.log_bin_coeff(num_active_self, num_diff)

        return log_liks

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (C) 2012-2016 GEM Foundation
#
# OpenQuake is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenQuake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake. If not, see <http://www.gnu.org/licenses/>.

# hazardlib documentation build configuration file, created by
# sphinx-quickstart on Fri May 18 17:26:22 2012.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys, os
import openquake.hazardlib

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))

# -- General configuration -----------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.viewcode']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'hazardlib'
copyright = u'2012-2016 GEM Foundation'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = openquake.hazardlib.__version__
# The full version, including alpha/beta/rc tags.
release = version

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []

# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# -- Options for HTML output ---------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'classic'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None

# Output file base name for HTML help builder.
htmlhelp_basename = 'hazardlibdoc'

# -- Options for LaTeX output --------------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#'preamble': '',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
  ('index', 'openquake.hazardlib.tex', u'hazardlib Documentation',
   u'GEM Foundation', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output --------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    ('index', 'hazardlib', u'hazardlib Documentation',
     [u'GEM Foundation'], 1)
]

# If true, show URL addresses after external links.
#man_show_urls = False

# -- Options for Texinfo output ------------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
  ('index', 'hazardlib', u'hazardlib Documentation',
   u'GEM Foundation', 'hazardlib', 'One line description of project.',
   'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
#texinfo_appendices = []

# If false, no module index is generated.
#texinfo_domain_indices = True

# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

nitpick_ignore = [
    ('py:obj', 'float'),
    ('py:obj', 'int'),
    ('py:obj', 'array'),
    ('py:exc', 'ValueError'),
]
<commit_msg>Exclude intro.rst from Sphinx toc<commit_after># -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (C) 2012-2016 GEM Foundation
#
# OpenQuake is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenQuake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake. If not, see <http://www.gnu.org/licenses/>.

# hazardlib documentation build configuration file, created by
# sphinx-quickstart on Fri May 18 17:26:22 2012.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys, os
import openquake.hazardlib

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))

# -- General configuration -----------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.viewcode']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'hazardlib'
copyright = u'2012-2016 GEM Foundation'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = openquake.hazardlib.__version__
# The full version, including alpha/beta/rc tags.
release = version

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['intro.rst']

# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# -- Options for HTML output ---------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'classic'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None

# Output file base name for HTML help builder.
htmlhelp_basename = 'hazardlibdoc'

# -- Options for LaTeX output --------------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#'preamble': '',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
  ('index', 'openquake.hazardlib.tex', u'hazardlib Documentation',
   u'GEM Foundation', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output --------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    ('index', 'hazardlib', u'hazardlib Documentation',
     [u'GEM Foundation'], 1)
]

# If true, show URL addresses after external links.
#man_show_urls = False

# -- Options for Texinfo output ------------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
  ('index', 'hazardlib', u'hazardlib Documentation',
   u'GEM Foundation', 'hazardlib', 'One line description of project.',
   'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
#texinfo_appendices = []

# If false, no module index is generated.
#texinfo_domain_indices = True

# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

nitpick_ignore = [
    ('py:obj', 'float'),
    ('py:obj', 'int'),
    ('py:obj', 'array'),
    ('py:exc', 'ValueError'),
]
<|endoftext|>


<commit_before>
"""
Implementation of the command-line I{flake8} tool.
"""
import sys
import os
import os.path
from subprocess import PIPE, Popen
import select
try:
    from StringIO import StringIO   # NOQA
except ImportError:
    from io import StringIO         # NOQA

from flake8.util import skip_file
from flake8 import pep8
from flake8 import pyflakes
from flake8 import mccabe

pep8style = None

def check_file(path, complexity=-1):
    warnings = pyflakes.checkPath(path)
    warnings += pep8style.input_file(path)
    if complexity > -1:
        warnings += mccabe.get_module_complexity(path, complexity)
    return warnings

def check_code(code, complexity=-1):
    warnings = pyflakes.check(code, 'stdin')
    warnings += pep8style.input_file(StringIO(code))
    if complexity > -1:
        warnings += mccabe.get_code_complexity(code, complexity)
    return warnings

def _get_python_files(paths):
    for path in paths:
        if os.path.isdir(path):
            for dirpath, dirnames, filenames in os.walk(path):
                for filename in filenames:
                    if not filename.endswith('.py'):
                        continue
                    fullpath = os.path.join(dirpath, filename)
                    if not skip_file(fullpath):
                        yield fullpath

        else:
            if not skip_file(path):
                yield path

def main():
    global pep8style
    pep8style = pep8.StyleGuide(parse_argv=True, config_file=True)
    options = pep8style.options
    complexity = options.max_complexity
    builtins = set(options.builtins)
    warnings = 0

    if builtins:
        orig_builtins = set(pyflakes._MAGIC_GLOBALS)
        pyflakes._MAGIC_GLOBALS = orig_builtins | builtins

    if pep8style.paths and options.filename is not None:
        for path in _get_python_files(pep8style.paths):
            warnings += check_file(path, complexity)
    else:
        # wait for 1 second on the stdin fd
        reads, __, __ = select.select([sys.stdin], [], [], 1.)
        if reads == []:
            print('input not specified')
            raise SystemExit(1)

        stdin = sys.stdin.read()
        warnings += check_code(stdin, complexity)

    if options.exit_zero:
        raise SystemExit(0)
    raise SystemExit(warnings > 0)

def _get_files(repo, **kwargs):
    seen = set()
    for rev in range(repo[kwargs['node']], len(repo)):
        for file_ in repo[rev].files():
            file_ = os.path.join(repo.root, file_)
            if file_ in seen or not os.path.exists(file_):
                continue
            seen.add(file_)
            if not file_.endswith('.py'):
                continue
            if skip_file(file_):
                continue
            yield file_

class _PEP8Options(object):
    # Default options taken from pep8.process_options()
    max_complexity = -1
    verbose = False
    quiet = False
    no_repeat = False
    exclude = [exc.rstrip('/') for exc in pep8.DEFAULT_EXCLUDE.split(',')]
    filename = ['*.py']
    select = []
    ignore = pep8.DEFAULT_IGNORE.split(',')  # or []?
    show_source = False
    show_pep8 = False
    statistics = False
    count = False
    benchmark = False
    testsuite = ''
    doctest = False
    max_line_length = pep8.MAX_LINE_LENGTH

def _initpep8():
    # default pep8 setup
    pep8.options = _PEP8Options()
    pep8.options.physical_checks = pep8.find_checks('physical_line')
    pep8.options.logical_checks = pep8.find_checks('logical_line')
    pep8.options.counters = dict.fromkeys(pep8.BENCHMARK_KEYS, 0)
    pep8.options.messages = {}
    pep8.options.max_line_length = 79
    pep8.args = []

def run(command):
    p = Popen(command.split(), stdout=PIPE, stderr=PIPE)
    p.wait()
    return (p.returncode, [line.strip() for line in p.stdout.readlines()],
            [line.strip() for line in p.stderr.readlines()])

def git_hook(complexity=-1, strict=False, ignore=None):
    global pep8style
    pep8style = pep8.StyleGuide(config_file=True)

    _initpep8()
    if ignore:
        pep8.options.ignore = ignore

    warnings = 0

    _, files_modified, _ = run("git diff-index --cached --name-only HEAD")
    for filename in files_modified:
        ext = os.path.splitext(filename)[-1]
        if ext != '.py':
            continue
        if not os.path.exists(filename):
            continue
        warnings += check_file(filename, complexity)

    if strict:
        return warnings

    return 0

def hg_hook(ui, repo, **kwargs):
    _initpep8()
    complexity = ui.configint('flake8', 'complexity', default=-1)
    warnings = 0

    for file_ in _get_files(repo, **kwargs):
        warnings += check_file(file_, complexity)

    strict = ui.configbool('flake8', 'strict', default=True)

    if strict:
        return warnings

    return 0

try:
    from setuptools import Command
except ImportError:
    Flake8Command = None
else:
    class Flake8Command(Command):
        description = "Run flake8 on modules registered in setuptools"
        user_options = []

        def initialize_options(self):
            pass

        def finalize_options(self):
            pass

        def distribution_files(self):
            if self.distribution.packages:
                for package in self.distribution.packages:
                    yield package.replace(".", os.path.sep)

            if self.distribution.py_modules:
                for filename in self.distribution.py_modules:
                    yield "%s.py" % filename

        def run(self):
            global pep8style
            pep8style = pep8.StyleGuide(config_file=True)

            _initpep8()

            # _get_python_files can produce the same file several
            # times, if one of its paths is a parent of another. Keep
            # a set of checked files to de-duplicate.
            checked = set()

            warnings = 0
            for path in _get_python_files(self.distribution_files()):
                if path not in checked:
                    warnings += check_file(path)
                checked.add(path)

            raise SystemExit(warnings > 0)

if __name__ == '__main__':
    main()
<commit_msg>Incorporate @phd's patch.<commit_after>
"""
Implementation of the command-line I{flake8} tool.
"""
import sys
import os
import os.path
from subprocess import PIPE, Popen
import select
try:
    from StringIO import StringIO   # NOQA
except ImportError:
    from io import StringIO         # NOQA

from flake8.util import skip_file
from flake8 import pep8
from flake8 import pyflakes
from flake8 import mccabe

pep8style = None

def check_file(path, complexity=-1):
    if pep8style.excluded(path):
        return 0
    warnings = pyflakes.checkPath(path)
    warnings += pep8style.input_file(path)
    if complexity > -1:
        warnings += mccabe.get_module_complexity(path, complexity)
    return warnings

def check_code(code, complexity=-1):
    warnings = pyflakes.check(code, 'stdin')
    warnings += pep8style.input_file(StringIO(code))
    if complexity > -1:
        warnings += mccabe.get_code_complexity(code, complexity)
    return warnings

def _get_python_files(paths):
    for path in paths:
        if os.path.isdir(path):
            for dirpath, dirnames, filenames in os.walk(path):
                for filename in filenames:
                    if not filename.endswith('.py'):
                        continue
                    fullpath = os.path.join(dirpath, filename)
                    if not skip_file(fullpath):
                        yield fullpath

        else:
            if not skip_file(path):
                yield path

def main():
    global pep8style
    pep8style = pep8.StyleGuide(parse_argv=True, config_file=True)
    options = pep8style.options
    complexity = options.max_complexity
    builtins = set(options.builtins)
    warnings = 0

    if builtins:
        orig_builtins = set(pyflakes._MAGIC_GLOBALS)
        pyflakes._MAGIC_GLOBALS = orig_builtins | builtins

    if pep8style.paths and options.filename is not None:
        for path in _get_python_files(pep8style.paths):
            warnings += check_file(path, complexity)
    else:
        # wait for 1 second on the stdin fd
        reads, __, __ = select.select([sys.stdin], [], [], 1.)
        if reads == []:
            print('input not specified')
            raise SystemExit(1)

        stdin = sys.stdin.read()
        warnings += check_code(stdin, complexity)

    if options.exit_zero:
        raise SystemExit(0)
    raise SystemExit(warnings > 0)

def _get_files(repo, **kwargs):
    seen = set()
    for rev in range(repo[kwargs['node']], len(repo)):
        for file_ in repo[rev].files():
            file_ = os.path.join(repo.root, file_)
            if file_ in seen or not os.path.exists(file_):
                continue
            seen.add(file_)
            if not file_.endswith('.py'):
                continue
            if skip_file(file_):
                continue
            yield file_

class _PEP8Options(object):
    # Default options taken from pep8.process_options()
    max_complexity = -1
    verbose = False
    quiet = False
    no_repeat = False
    exclude = [exc.rstrip('/') for exc in pep8.DEFAULT_EXCLUDE.split(',')]
    filename = ['*.py']
    select = []
    ignore = pep8.DEFAULT_IGNORE.split(',')  # or []?
    show_source = False
    show_pep8 = False
    statistics = False
    count = False
    benchmark = False
    testsuite = ''
    doctest = False
    max_line_length = pep8.MAX_LINE_LENGTH

def _initpep8():
    # default pep8 setup
    pep8.options = _PEP8Options()
    pep8.options.physical_checks = pep8.find_checks('physical_line')
    pep8.options.logical_checks = pep8.find_checks('logical_line')
    pep8.options.counters = dict.fromkeys(pep8.BENCHMARK_KEYS, 0)
    pep8.options.messages = {}
    pep8.options.max_line_length = 79
    pep8.args = []

def run(command):
    p = Popen(command.split(), stdout=PIPE, stderr=PIPE)
    p.wait()
    return (p.returncode, [line.strip() for line in p.stdout.readlines()],
            [line.strip() for line in p.stderr.readlines()])

def git_hook(complexity=-1, strict=False, ignore=None):
    global pep8style
    pep8style = pep8.StyleGuide(config_file=True)

    _initpep8()
    if ignore:
        pep8.options.ignore = ignore

    warnings = 0

    _, files_modified, _ = run("git diff-index --cached --name-only HEAD")
    for filename in files_modified:
        ext = os.path.splitext(filename)[-1]
        if ext != '.py':
            continue
        if not os.path.exists(filename):
            continue
        warnings += check_file(filename, complexity)

    if strict:
        return warnings

    return 0

def hg_hook(ui, repo, **kwargs):
    _initpep8()
    complexity = ui.configint('flake8', 'complexity', default=-1)
    warnings = 0

    for file_ in _get_files(repo, **kwargs):
        warnings += check_file(file_, complexity)

    strict = ui.configbool('flake8', 'strict', default=True)

    if strict:
        return warnings

    return 0

try:
    from setuptools import Command
except ImportError:
    Flake8Command = None
else:
    class Flake8Command(Command):
        description = "Run flake8 on modules registered in setuptools"
        user_options = []

        def initialize_options(self):
            pass

        def finalize_options(self):
            pass

        def distribution_files(self):
            if self.distribution.packages:
                for package in self.distribution.packages:
                    yield package.replace(".", os.path.sep)

            if self.distribution.py_modules:
                for filename in self.distribution.py_modules:
                    yield "%s.py" % filename

        def run(self):
            global pep8style
            pep8style = pep8.StyleGuide(config_file=True)

            _initpep8()

            # _get_python_files can produce the same file several
            # times, if one of its paths is a parent of another. Keep
            # a set of checked files to de-duplicate.
            checked = set()

            warnings = 0
            for path in _get_python_files(self.distribution_files()):
                if path not in checked:
                    warnings += check_file(path)
                checked.add(path)

            raise SystemExit(warnings > 0)

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


<commit_before>"""Contains all the steps available."""

import select
import shlex
import subprocess

from . import events

class StopTask(StopIteration):
    pass

def command(project, task, step, variables):
    command_line = step.args.format(**variables)
    command_args = shlex.split(command_line)

    yield events.running_command(command_args)

    process = subprocess.Popen(
        command_args, shell=True, universal_newlines=True, bufsize=1,
        stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    )

    streams = ['stdout', 'stderr']

    def send_line(stream, line):
        line = line.strip()
        if line:
            yield events.command_output(stream, line)

    def flush_line(fd, stream):
        if fd == getattr(process, stream).fileno():
            line = getattr(process, stream).readline()
            yield from send_line(stream, line)

    while True:
        reads = [getattr(process, stream).fileno() for stream in streams]
        ret = select.select(reads, [], [])

        for fd in ret[0]:
            for stream in streams:
                flush_line(fd, streams)

        if process.poll() != None:
            break

    def flush_remaining_lines(stream):
        for line in getattr(process, stream).readlines():
            yield from send_line(stream, line)

    for stream in streams:
        yield from flush_remaining_lines(stream)

    if process.returncode != 0:
        yield events.command_failed(command_args, process.returncode)
        raise StopTask

def help(project, task, step, variables):
    """Run a help step."""

    task_name = step.args or variables['task']

    try:
        task = project.find_task(task_name)
    except NoSuchTaskError as e:
        yield events.task_not_found(task_name, e.similarities)
        raise StopTask

    text = f'# {task.name}\n'
    text += '\n'
    text += task.description
    text += '\n\n'
    text += 'Variables: {}'.format(', '.join(task.variables))

    yield events.help_step_output(text)
<commit_msg>Fix command step<commit_after>"""Contains all the steps available."""

import select
import shlex
import subprocess

from . import events

class StopTask(StopIteration):
    pass

def command(project, task, step, variables):
    command_line = step.args.format(**variables)
    command_args = shlex.split(command_line)

    yield events.running_command(command_args)

    process = subprocess.Popen(
        command_line, shell=True, universal_newlines=True, bufsize=1,
        stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    )

    streams = ['stdout', 'stderr']

    def send_line(stream, line):
        line = line.strip()
        if line:
            yield events.command_output(stream, line)

    def flush_line(fd, stream):
        if fd == getattr(process, stream).fileno():
            line = getattr(process, stream).readline()
            yield from send_line(stream, line)

    while True:
        reads = [getattr(process, stream).fileno() for stream in streams]
        ret = select.select(reads, [], [])

        for fd in ret[0]:
            for stream in streams:
                flush_line(fd, streams)

        if process.poll() != None:
            break

    def flush_remaining_lines(stream):
        for line in getattr(process, stream).readlines():
            yield from send_line(stream, line)

    for stream in streams:
        yield from flush_remaining_lines(stream)

    if process.returncode != 0:
        yield events.command_failed(command_args, process.returncode)
        raise StopTask

def help(project, task, step, variables):
    """Run a help step."""

    task_name = step.args or variables['task']

    try:
        task = project.find_task(task_name)
    except NoSuchTaskError as e:
        yield events.task_not_found(task_name, e.similarities)
        raise StopTask

    text = f'# {task.name}\n'
    text += '\n'
    text += task.description
    text += '\n\n'
    text += 'Variables: {}'.format(', '.join(task.variables))

    yield events.help_step_output(text)
<|endoftext|>


<commit_before>
"""
Implementation of the command-line I{flake8} tool.
"""
import sys
import os
import os.path
from subprocess import PIPE, Popen
import select
try:
    from StringIO import StringIO   # NOQA
except ImportError:
    from io import StringIO         # NOQA

from flake8.util import skip_file
from flake8 import pep8
from flake8 import pyflakes
from flake8 import mccabe

pep8style = None

def check_file(path, complexity=-1):
    warnings = pyflakes.checkPath(path)
    warnings += pep8style.input_file(path)
    if complexity > -1:
        warnings += mccabe.get_module_complexity(path, complexity)
    return warnings

def check_code(code, complexity=-1):
    warnings = pyflakes.check(code, 'stdin')
    warnings += pep8style.input_file(StringIO(code))
    if complexity > -1:
        warnings += mccabe.get_code_complexity(code, complexity)
    return warnings

def _get_python_files(paths):
    seen = set()
    for path in paths:
        if os.path.isdir(path):
            for dirpath, dirnames, filenames in os.walk(path):
                for filename in filenames:
                    if not filename.endswith('.py'):
                        continue
                    fullpath = os.path.join(dirpath, filename)
                    if not skip_file(fullpath) and fullpath not in seen:
                        yield fullpath

        else:
            if not skip_file(path) and path not in seen:
                yield path

def main():
    global pep8style
    pep8style = pep8.StyleGuide(parse_argv=True, config_file=True)
    options = pep8style.options
    complexity = options.max_complexity
    builtins = set(options.builtins)
    warnings = 0

    if builtins:
        orig_builtins = set(pyflakes._MAGIC_GLOBALS)
        pyflakes._MAGIC_GLOBALS = orig_builtins | builtins

    if pep8style.paths and options.filename is not None:
        for path in _get_python_files(pep8style.paths):
            warnings += check_file(path, complexity)
    else:
        # wait for 1 second on the stdin fd
        reads, __, __ = select.select([sys.stdin], [], [], 1.)
        if reads == []:
            print('input not specified')
            raise SystemExit(1)

        stdin = sys.stdin.read()
        warnings += check_code(stdin, complexity)

    if options.exit_zero:
        raise SystemExit(0)
    raise SystemExit(warnings > 0)

def _get_files(repo, **kwargs):
    seen = set()
    for rev in range(repo[kwargs['node']], len(repo)):
        for file_ in repo[rev].files():
            file_ = os.path.join(repo.root, file_)
            if file_ in seen or not os.path.exists(file_):
                continue
            seen.add(file_)
            if not file_.endswith('.py'):
                continue
            if skip_file(file_):
                continue
            yield file_

class _PEP8Options(object):
    # Default options taken from pep8.process_options()
    max_complexity = -1
    verbose = False
    quiet = False
    no_repeat = False
    exclude = [exc.rstrip('/') for exc in pep8.DEFAULT_EXCLUDE.split(',')]
    filename = ['*.py']
    select = []
    ignore = pep8.DEFAULT_IGNORE.split(',')  # or []?
    show_source = False
    show_pep8 = False
    statistics = False
    count = False
    benchmark = False
    testsuite = ''
    doctest = False
    max_line_length = pep8.MAX_LINE_LENGTH

def _initpep8():
    # default pep8 setup
    pep8.options = _PEP8Options()
    pep8.options.physical_checks = pep8.find_checks('physical_line')
    pep8.options.logical_checks = pep8.find_checks('logical_line')
    pep8.options.counters = dict.fromkeys(pep8.BENCHMARK_KEYS, 0)
    pep8.options.messages = {}
    pep8.options.max_line_length = 79
    pep8.args = []

def run(command):
    p = Popen(command.split(), stdout=PIPE, stderr=PIPE)
    p.wait()
    return (p.returncode, [line.strip() for line in p.stdout.readlines()],
            [line.strip() for line in p.stderr.readlines()])

def git_hook(complexity=-1, strict=False, ignore=None):
    _initpep8()
    if ignore:
        pep8.options.ignore=ignore

    warnings = 0

    _, files_modified, _ = run("git diff-index --cached --name-only HEAD")
    for filename in files_modified:
        ext = os.path.splitext(filename)[-1]
        if ext != '.py':
            continue
        if not os.path.exists(filename):
            continue
        warnings += check_file(filename, complexity)

    if strict:
        return warnings

    return 0

def hg_hook(ui, repo, **kwargs):
    _initpep8()
    complexity = ui.configint('flake8', 'complexity', default=-1)
    warnings = 0

    for file_ in _get_files(repo, **kwargs):
        warnings += check_file(file_, complexity)

    strict = ui.configbool('flake8', 'strict', default=True)

    if strict:
        return warnings

    return 0

try:
    from setuptools import Command
except ImportError:
    Flake8Command = None
else:
    class Flake8Command(Command):
        description = "Run flake8 on modules registered in setuptools"
        user_options = []

        def initialize_options(self):
            pass

        def finalize_options(self):
            pass

        def distribution_files(self):
            if self.distribution.packages:
                for package in self.distribution.packages:
                    yield package.replace(".", os.path.sep)

            if self.distribution.py_modules:
                for filename in self.distribution.py_modules:
                    yield "%s.py" % filename

        def run(self):
            global pep8style
            pep8style = pep8.StyleGuide(config_file=True)

            _initpep8()

            # _get_python_files can produce the same file several
            # times, if one of its paths is a parent of another. Keep
            # a set of checked files to de-duplicate.
            checked = set()

            warnings = 0
            for path in _get_python_files(self.distribution_files()):
                if path not in checked:
                    warnings += check_file(path)
                checked.add(path)

            raise SystemExit(warnings > 0)

if __name__ == '__main__':
    main()
<commit_msg>Remove the now-unused set() in _get_python_files<commit_after>
"""
Implementation of the command-line I{flake8} tool.
"""
import sys
import os
import os.path
from subprocess import PIPE, Popen
import select
try:
    from StringIO import StringIO   # NOQA
except ImportError:
    from io import StringIO         # NOQA

from flake8.util import skip_file
from flake8 import pep8
from flake8 import pyflakes
from flake8 import mccabe

pep8style = None

def check_file(path, complexity=-1):
    warnings = pyflakes.checkPath(path)
    warnings += pep8style.input_file(path)
    if complexity > -1:
        warnings += mccabe.get_module_complexity(path, complexity)
    return warnings

def check_code(code, complexity=-1):
    warnings = pyflakes.check(code, 'stdin')
    warnings += pep8style.input_file(StringIO(code))
    if complexity > -1:
        warnings += mccabe.get_code_complexity(code, complexity)
    return warnings

def _get_python_files(paths):
    for path in paths:
        if os.path.isdir(path):
            for dirpath, dirnames, filenames in os.walk(path):
                for filename in filenames:
                    if not filename.endswith('.py'):
                        continue
                    fullpath = os.path.join(dirpath, filename)
                    if not skip_file(fullpath):
                        yield fullpath

        else:
            if not skip_file(path):
                yield path

def main():
    global pep8style
    pep8style = pep8.StyleGuide(parse_argv=True, config_file=True)
    options = pep8style.options
    complexity = options.max_complexity
    builtins = set(options.builtins)
    warnings = 0

    if builtins:
        orig_builtins = set(pyflakes._MAGIC_GLOBALS)
        pyflakes._MAGIC_GLOBALS = orig_builtins | builtins

    if pep8style.paths and options.filename is not None:
        for path in _get_python_files(pep8style.paths):
            warnings += check_file(path, complexity)
    else:
        # wait for 1 second on the stdin fd
        reads, __, __ = select.select([sys.stdin], [], [], 1.)
        if reads == []:
            print('input not specified')
            raise SystemExit(1)

        stdin = sys.stdin.read()
        warnings += check_code(stdin, complexity)

    if options.exit_zero:
        raise SystemExit(0)
    raise SystemExit(warnings > 0)

def _get_files(repo, **kwargs):
    seen = set()
    for rev in range(repo[kwargs['node']], len(repo)):
        for file_ in repo[rev].files():
            file_ = os.path.join(repo.root, file_)
            if file_ in seen or not os.path.exists(file_):
                continue
            seen.add(file_)
            if not file_.endswith('.py'):
                continue
            if skip_file(file_):
                continue
            yield file_

class _PEP8Options(object):
    # Default options taken from pep8.process_options()
    max_complexity = -1
    verbose = False
    quiet = False
    no_repeat = False
    exclude = [exc.rstrip('/') for exc in pep8.DEFAULT_EXCLUDE.split(',')]
    filename = ['*.py']
    select = []
    ignore = pep8.DEFAULT_IGNORE.split(',')  # or []?
    show_source = False
    show_pep8 = False
    statistics = False
    count = False
    benchmark = False
    testsuite = ''
    doctest = False
    max_line_length = pep8.MAX_LINE_LENGTH

def _initpep8():
    # default pep8 setup
    pep8.options = _PEP8Options()
    pep8.options.physical_checks = pep8.find_checks('physical_line')
    pep8.options.logical_checks = pep8.find_checks('logical_line')
    pep8.options.counters = dict.fromkeys(pep8.BENCHMARK_KEYS, 0)
    pep8.options.messages = {}
    pep8.options.max_line_length = 79
    pep8.args = []

def run(command):
    p = Popen(command.split(), stdout=PIPE, stderr=PIPE)
    p.wait()
    return (p.returncode, [line.strip() for line in p.stdout.readlines()],
            [line.strip() for line in p.stderr.readlines()])

def git_hook(complexity=-1, strict=False, ignore=None):
    _initpep8()
    if ignore:
        pep8.options.ignore=ignore

    warnings = 0

    _, files_modified, _ = run("git diff-index --cached --name-only HEAD")
    for filename in files_modified:
        ext = os.path.splitext(filename)[-1]
        if ext != '.py':
            continue
        if not os.path.exists(filename):
            continue
        warnings += check_file(filename, complexity)

    if strict:
        return warnings

    return 0

def hg_hook(ui, repo, **kwargs):
    _initpep8()
    complexity = ui.configint('flake8', 'complexity', default=-1)
    warnings = 0

    for file_ in _get_files(repo, **kwargs):
        warnings += check_file(file_, complexity)

    strict = ui.configbool('flake8', 'strict', default=True)

    if strict:
        return warnings

    return 0

try:
    from setuptools import Command
except ImportError:
    Flake8Command = None
else:
    class Flake8Command(Command):
        description = "Run flake8 on modules registered in setuptools"
        user_options = []

        def initialize_options(self):
            pass

        def finalize_options(self):
            pass

        def distribution_files(self):
            if self.distribution.packages:
                for package in self.distribution.packages:
                    yield package.replace(".", os.path.sep)

            if self.distribution.py_modules:
                for filename in self.distribution.py_modules:
                    yield "%s.py" % filename

        def run(self):
            global pep8style
            pep8style = pep8.StyleGuide(config_file=True)

            _initpep8()

            # _get_python_files can produce the same file several
            # times, if one of its paths is a parent of another. Keep
            # a set of checked files to de-duplicate.
            checked = set()

            warnings = 0
            for path in _get_python_files(self.distribution_files()):
                if path not in checked:
                    warnings += check_file(path)
                checked.add(path)

            raise SystemExit(warnings > 0)

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


<commit_before># -*- coding: utf-8 -*-
"""
    flask_cors
    ~~~~
    Flask-CORS is a simple extension to Flask allowing you to support cross
    origin resource sharing (CORS) using a simple decorator.

    :copyright: (c) 2014 by Cory Dolphin.
    :license: MIT, see LICENSE for more details.
"""
import collections
from datetime import timedelta
import re
from functools import update_wrapper

from flask import make_response, request, current_app
from six import string_types

# Common string constants
ACL_ORIGIN = 'Access-Control-Allow-Origin'
ACL_METHODS = 'Access-Control-Allow-Methods'
ACL_HEADERS = 'Access-Control-Allow-Headers'
ACL_EXPOSE_HEADERS = 'Access-Control-Expose-Headers'
ACL_CREDENTIALS = 'Access-Control-Allow-Credentials'
ACL_MAX_AGE = 'Access-Control-Max-Age'

ALL_METHODS = ['GET', 'HEAD', 'POST', 'OPTIONS', 'PUT', 'PATCH', 'DELETE']

CONFIG_OPTIONS = ['CORS_ORIGINS', 'CORS_METHODS', 'CORS_HEADERS',
                  'CORS_EXPOSE_HEADERS', 'CORS_SUPPORTS_CREDENTIALS',
                  'CORS_MAX_AGE', 'CORS_SEND_WILDCARD', 'CORS_ALWAYS_SEND',
                  'CORS_AUTOMATIC_OPTIONS', 'CORS_VARY_HEADER']

_defaults_dict = dict(origins='*',
                      always_send=True,
                      automatic_options=True,
                      send_wildcard=True,
                      vary_header=True)

def cross_origin(*args, **kwargs):
    '''
    This function is the decorator which is used to wrap a Flask route with.
    In the simplest case, simply use the default parameters to allow all
    origins in what is the most permissive configuration. If this method
    modifies state or performs authentication which may be brute-forced, you
    should add some degree of protection, such as Cross Site Forgery
    Request protection.

    :param origins: The origin, or list of origins to allow requests from.
    :type origins: list or string

    :param methods: The method or list of methods which the allowed origins
        are allowed to access.
    :type methods: list

    :param headers: The header or list of header field names which can be used
        when this resource is accessed by allowed origins.
    :type headers: list or string

    :param expose_headers: The header or list of headers which are are safe to
        expose to browsers.
    :type headers: list or string

    :param supports_credentials: Allows users to make authenticated requests.
        If true, injects the `Access-Control-Allow-Credentials` header in
        responses.
        Note: According to the W3 spec, this option cannot be used in
        conjuction with a '*' origin

    :type supports_credentials: bool

    :param max_age: The maximum time for which this CORS request maybe cached.
        This value is set as the `Access-Control-Max-Age` header.
    :type max_age: timedelta, integer, string or None

    :param send_wildcard: If True, and the origins parameter is `*`, a
        wildcard `Access-Control-Allow-Origin` header is sent, rather than
        the request's `Origin` header.
    :type send_wildcard: bool

    :param always_send: If True, CORS headers are sent even if there is no
                        `Origin` in the request's headers.
    :type always_send: bool

    :param automatic_options: If True, CORS headers will be returned for
        OPTIONS requests. For use with cross domain POST requests which
        preflight OPTIONS requests, you will need to specifically allow
        the Content-Type header.
    :type automatic_options: bool

    :param vary_header: If True, the header Vary: Origin will be returned
        as per suggestion by the W3 implementation guidelines. Setting this
        header when the `Access-Control-Allow-Origin` is dynamically generated
        e.g. when there is more than one allowed origin, and any Origin other
        than '*' is returned, informing CDNs and other caches that the CORS
        headers are dynamic, and cannot be re-used.
        If False, the Vary header will never be injected or altered.
    :type vary_header: bool

    '''

    _options = kwargs

    def decorator(f):
        def wrapped_function(*args, **kwargs):

            # Handle setting of Flask-Cors parameters
            options = {}
            options.update(_defaults_dict)
            options.update(_get_app_kwarg_dict())
            options.update(_options)
            _serialize_options(options)

            if options.get('automatic_options') and request.method == 'OPTIONS':
                resp = current_app.make_default_options_response()
            else:
                resp = make_response(f(*args, **kwargs))

            _set_cors_headers(resp, options)
            resp._FLASK_CORS_EVALUATED = True  # Mark response as evaluated

            return resp

        # If True, intercept OPTIONS requests by modifying the view function
        # replicating Flask's default behavior, and wrapping the response with
        # CORS headers.
        #
        # If f.provide_automatic_options is unset or True, Flask's route
        # decorator (which is actually wraps the function object we return)
        # intercepts OPTIONS handling, and requests will not have CORS headers
        if _options.get('automatic_options', True):
            f.required_methods = getattr(f, 'required_methods', set())
            f.required_methods.add('OPTIONS')
            f.provide_automatic_options = False

        return update_wrapper(wrapped_function, f)
    return decorator

class CORS(object):
    def __init__(self, app=None, **kwargs):
        '''
            Initializes Cross Origin Resource sharing for the application. The
            arguments are identical to :py:func:`cross_origin`, with the
            addition of a `resources` parameter. The resources parameter
            defines a series of regular expressions for resource paths to match
            and optionally, the associated :py:func:`cross_origin` options
            to be applied to the particular resource.

            The settings for CORS are determined in the following order:
                Resource level settings (e.g when passed as a dictionary)
                Keyword argument settings
                App level configuration settings (e.g. CORS_*)
                Default settings

            Note: as it is possible for multiple regular expressions to match a
            resource path, the regular expressions are first sorted by length,
            from longest to shortest, in order to attempt to match the most
            specific regular expression. This allows the definition of a
            number of specific resource options, with a wildcard fallback
            for all other resources.

            :param resources: the series of regular expression and (optionally)
            associated CORS options to be applied to the given resource path.

            If the argument is a dictionary, it is expected to be of the form:
            regexp : dict_of_options

            If the argument is a list, it is expected to be a list of regular
            expressions, for which the app-wide configured options are applied.

            If the argument is a string, it is expected to be a regular
            expression for which the app-wide configured options are applied.
            :type resources: dict, iterable or string

        '''

        if app is not None:
            self.init_app(app, **kwargs)

    def init_app(self, app, **kwargs):
        options = {}
        options.update(_defaults_dict)
        options.update(_get_app_kwarg_dict(app))
        options.update(kwargs)

        _kwarg_resources = kwargs.get('resources')
        _app_resources = app.config.get('CORS_RESOURCES')
        _resources = _kwarg_resources or _app_resources or [r'/*']

        if isinstance(_resources, dict):  # sort the regexps by length
            resources = sorted(_resources.items(),
                               key=lambda r: len(r[0]),
                               reverse=True
                               )
        elif isinstance(_resources, string_types):
            resources = [(_resources, {})]
        elif isinstance(_resources, collections.Iterable):
            resources = [(r, {}) for r in _resources]
        else:
            raise ValueError("Unexpected value for resources argument.")

        def cors_after_request(resp):
            '''
                The actual after-request handler, retains references to the
                the options, and definitions of resources through a closure.
            '''
            # If CORS headers are set in a view decorator, pass
            if resp.headers.get(ACL_ORIGIN):
                return resp

            for res_regex, res_options in resources:
                if re.match(res_regex, request.path):
                    _options = options.copy()
                    _options.update(res_options)
                    _serialize_options(_options)
                    _set_cors_headers(resp, _options)
                    break
            return resp

        app.after_request(cors_after_request)

def _set_cors_headers(resp, options):
    '''
        Performs the actual evaluation of Flas-CORS options and actually
        modifies the response object.

        This function is used both in the decorator and the after_request
        callback
    '''

    # If CORS has already been evaluated via the decorator, skip
    if hasattr(resp, '_FLASK_CORS_EVALUATED'):
        return resp

    request_origin = request.headers.get('Origin', None)
    wildcard = options.get('origins') == '*'
    # If the Origin header is not present terminate this set of steps.
    # The request is outside the scope of this specification.-- W3Spec
    if request_origin:

        # If the allowed origins is an asterisk or 'wildcard', always match
        if wildcard:
            if options.get('send_wildcard'):
                resp.headers[ACL_ORIGIN] = '*'
            else:
                resp.headers[ACL_ORIGIN] = request_origin

        # If the value of the Origin header is a case-sensitive match
        # for any of the values in list of origins
        elif request_origin in options.get('origins'):
            # Add a single Access-Control-Allow-Origin header, with either
            # the value of the Origin header or the string "*" as value.
            # -- W3Spec
            resp.headers[ACL_ORIGIN] = request_origin
        else:
            return resp

    # Unless always_send is set, then ignore W3 spec
    elif options.get('always_send'):
        resp.headers[ACL_ORIGIN] = options.get('origins')
    # Terminate these steps, return the original request untouched.
    else:
        return resp

    if options.get('methods'):
        resp.headers[ACL_METHODS] = options.get('methods')

    if options.get('headers'):
        resp.headers[ACL_HEADERS] = options.get('headers')

    if options.get('expose_headers'):
        resp.headers[ACL_EXPOSE_HEADERS] = options.get(
            'expose_headers')

    if options.get('max_age'):
        resp.headers[ACL_MAX_AGE] = options.get('max_age')

    if options.get('supports_credentials'):
        resp.headers[ACL_CREDENTIALS] = 'true'

    if request.method == 'OPTIONS':
        resp.headers[ACL_METHODS] = options.get('methods',
                                                _flexible_str(ALL_METHODS))

    # http://www.w3.org/TR/cors/#resource-implementation
    if resp.headers[ACL_ORIGIN] != '*' and options.get('vary_header'):
        vary = ['Origin', resp.headers.get('Vary', None)]
        resp.headers['Vary'] = ', '. join(v for v in vary if v is not None)

def _get_app_kwarg_dict(app=current_app):
    return dict([
                (k.lower().replace('cors_', ''), app.config.get(k))
                for k in CONFIG_OPTIONS
                if app.config.get(k) is not None
                ])

def _flexible_str(obj):
    if(not isinstance(obj, string_types)
            and isinstance(obj, collections.Iterable)):
        return ', '.join(str(item) for item in sorted(obj))
    else:
        return str(obj)

def _serialize_option(d, key, upper=False):
    if key in d:
        d[key] = _flexible_str(d[key])
        if upper:
            if d[key]:
                d[key].upper()

def _serialize_options(options):
    _serialize_option(options, 'methods', upper=True)
    _serialize_option(options, 'origins')
    _serialize_option(options, 'headers')
    _serialize_option(options, 'expose_headers')

    if isinstance(options.get('max_age'), timedelta):
        options['max_age'] = str(int(options['max_age'].total_seconds()))
<commit_msg>Adds support for regex origins<commit_after># -*- coding: utf-8 -*-
"""
    flask_cors
    ~~~~
    Flask-CORS is a simple extension to Flask allowing you to support cross
    origin resource sharing (CORS) using a simple decorator.

    :copyright: (c) 2014 by Cory Dolphin.
    :license: MIT, see LICENSE for more details.
"""
import collections
from datetime import timedelta
import re
from functools import update_wrapper

from flask import make_response, request, current_app
from six import string_types

# Common string constants
ACL_ORIGIN = 'Access-Control-Allow-Origin'
ACL_METHODS = 'Access-Control-Allow-Methods'
ACL_HEADERS = 'Access-Control-Allow-Headers'
ACL_EXPOSE_HEADERS = 'Access-Control-Expose-Headers'
ACL_CREDENTIALS = 'Access-Control-Allow-Credentials'
ACL_MAX_AGE = 'Access-Control-Max-Age'

ALL_METHODS = ['GET', 'HEAD', 'POST', 'OPTIONS', 'PUT', 'PATCH', 'DELETE']

CONFIG_OPTIONS = ['CORS_ORIGINS', 'CORS_METHODS', 'CORS_HEADERS',
                  'CORS_EXPOSE_HEADERS', 'CORS_SUPPORTS_CREDENTIALS',
                  'CORS_MAX_AGE', 'CORS_SEND_WILDCARD', 'CORS_ALWAYS_SEND',
                  'CORS_AUTOMATIC_OPTIONS', 'CORS_VARY_HEADER']

_defaults_dict = dict(origins='*',
                      always_send=True,
                      automatic_options=True,
                      send_wildcard=True,
                      vary_header=True)

def cross_origin(*args, **kwargs):
    '''
    This function is the decorator which is used to wrap a Flask route with.
    In the simplest case, simply use the default parameters to allow all
    origins in what is the most permissive configuration. If this method
    modifies state or performs authentication which may be brute-forced, you
    should add some degree of protection, such as Cross Site Forgery
    Request protection.

    :param origins: The origin, or list of origins to allow requests from.
    :type origins: list or string

    :param methods: The method or list of methods which the allowed origins
        are allowed to access.
    :type methods: list

    :param headers: The header or list of header field names which can be used
        when this resource is accessed by allowed origins.
    :type headers: list or string

    :param expose_headers: The header or list of headers which are are safe to
        expose to browsers.
    :type headers: list or string

    :param supports_credentials: Allows users to make authenticated requests.
        If true, injects the `Access-Control-Allow-Credentials` header in
        responses.
        Note: According to the W3 spec, this option cannot be used in
        conjuction with a '*' origin

    :type supports_credentials: bool

    :param max_age: The maximum time for which this CORS request maybe cached.
        This value is set as the `Access-Control-Max-Age` header.
    :type max_age: timedelta, integer, string or None

    :param send_wildcard: If True, and the origins parameter is `*`, a
        wildcard `Access-Control-Allow-Origin` header is sent, rather than
        the request's `Origin` header.
    :type send_wildcard: bool

    :param always_send: If True, CORS headers are sent even if there is no
                        `Origin` in the request's headers.
    :type always_send: bool

    :param automatic_options: If True, CORS headers will be returned for
        OPTIONS requests. For use with cross domain POST requests which
        preflight OPTIONS requests, you will need to specifically allow
        the Content-Type header.
    :type automatic_options: bool

    :param vary_header: If True, the header Vary: Origin will be returned
        as per suggestion by the W3 implementation guidelines. Setting this
        header when the `Access-Control-Allow-Origin` is dynamically generated
        e.g. when there is more than one allowed origin, and any Origin other
        than '*' is returned, informing CDNs and other caches that the CORS
        headers are dynamic, and cannot be re-used.
        If False, the Vary header will never be injected or altered.
    :type vary_header: bool

    '''

    _options = kwargs

    def decorator(f):
        def wrapped_function(*args, **kwargs):

            # Handle setting of Flask-Cors parameters
            options = {}
            options.update(_defaults_dict)
            options.update(_get_app_kwarg_dict())
            options.update(_options)
            _serialize_options(options)

            if options.get('automatic_options') and request.method == 'OPTIONS':
                resp = current_app.make_default_options_response()
            else:
                resp = make_response(f(*args, **kwargs))

            _set_cors_headers(resp, options)
            resp._FLASK_CORS_EVALUATED = True  # Mark response as evaluated

            return resp

        # If True, intercept OPTIONS requests by modifying the view function
        # replicating Flask's default behavior, and wrapping the response with
        # CORS headers.
        #
        # If f.provide_automatic_options is unset or True, Flask's route
        # decorator (which is actually wraps the function object we return)
        # intercepts OPTIONS handling, and requests will not have CORS headers
        if _options.get('automatic_options', True):
            f.required_methods = getattr(f, 'required_methods', set())
            f.required_methods.add('OPTIONS')
            f.provide_automatic_options = False

        return update_wrapper(wrapped_function, f)
    return decorator

class CORS(object):
    def __init__(self, app=None, **kwargs):
        '''
            Initializes Cross Origin Resource sharing for the application. The
            arguments are identical to :py:func:`cross_origin`, with the
            addition of a `resources` parameter. The resources parameter
            defines a series of regular expressions for resource paths to match
            and optionally, the associated :py:func:`cross_origin` options
            to be applied to the particular resource.

            The settings for CORS are determined in the following order:
                Resource level settings (e.g when passed as a dictionary)
                Keyword argument settings
                App level configuration settings (e.g. CORS_*)
                Default settings

            Note: as it is possible for multiple regular expressions to match a
            resource path, the regular expressions are first sorted by length,
            from longest to shortest, in order to attempt to match the most
            specific regular expression. This allows the definition of a
            number of specific resource options, with a wildcard fallback
            for all other resources.

            :param resources: the series of regular expression and (optionally)
            associated CORS options to be applied to the given resource path.

            If the argument is a dictionary, it is expected to be of the form:
            regexp : dict_of_options

            If the argument is a list, it is expected to be a list of regular
            expressions, for which the app-wide configured options are applied.

            If the argument is a string, it is expected to be a regular
            expression for which the app-wide configured options are applied.
            :type resources: dict, iterable or string

        '''

        if app is not None:
            self.init_app(app, **kwargs)

    def init_app(self, app, **kwargs):
        options = {}
        options.update(_defaults_dict)
        options.update(_get_app_kwarg_dict(app))
        options.update(kwargs)

        _kwarg_resources = kwargs.get('resources')
        _app_resources = app.config.get('CORS_RESOURCES')
        _resources = _kwarg_resources or _app_resources or [r'/*']

        if isinstance(_resources, dict):  # sort the regexps by length
            resources = sorted(_resources.items(),
                               key=lambda r: len(r[0]),
                               reverse=True
                               )
        elif isinstance(_resources, string_types):
            resources = [(_resources, {})]
        elif isinstance(_resources, collections.Iterable):
            resources = [(r, {}) for r in _resources]
        else:
            raise ValueError("Unexpected value for resources argument.")

        def cors_after_request(resp):
            '''
                The actual after-request handler, retains references to the
                the options, and definitions of resources through a closure.
            '''
            # If CORS headers are set in a view decorator, pass
            if resp.headers.get(ACL_ORIGIN):
                return resp

            for res_regex, res_options in resources:
                if re.match(res_regex, request.path):
                    _options = options.copy()
                    _options.update(res_options)
                    _serialize_options(_options)
                    _set_cors_headers(resp, _options)
                    break
            return resp

        app.after_request(cors_after_request)

def _set_cors_headers(resp, options):
    '''
        Performs the actual evaluation of Flas-CORS options and actually
        modifies the response object.

        This function is used both in the decorator and the after_request
        callback
    '''

    # If CORS has already been evaluated via the decorator, skip
    if hasattr(resp, '_FLASK_CORS_EVALUATED'):
        return resp

    request_origin = request.headers.get('Origin', None)
    origins = options.get('origins')
    wildcard = '*' in origins
    # If the Origin header is not present terminate this set of steps.
    # The request is outside the scope of this specification.-- W3Spec
    if request_origin:

        # If the allowed origins is an asterisk or 'wildcard', always match
        if wildcard:
            if options.get('send_wildcard'):
                resp.headers[ACL_ORIGIN] = '*'
            else:
                resp.headers[ACL_ORIGIN] = request_origin

        # If the value of the Origin header is a case-sensitive match
        # for any of the values in list of origins
        elif any(_try_match(pattern, request_origin) for pattern in origins):
            # Add a single Access-Control-Allow-Origin header, with either
            # the value of the Origin header or the string "*" as value.
            # -- W3Spec
            resp.headers[ACL_ORIGIN] = request_origin
        else:
            return resp

    # Unless always_send is set, then ignore W3 spec
    elif options.get('always_send'):
        resp.headers[ACL_ORIGIN] = options.get('origins_str')
    # Terminate these steps, return the original request untouched.
    else:
        return resp

    if options.get('methods'):
        resp.headers[ACL_METHODS] = options.get('methods')

    if options.get('headers'):
        resp.headers[ACL_HEADERS] = options.get('headers')

    if options.get('expose_headers'):
        resp.headers[ACL_EXPOSE_HEADERS] = options.get(
            'expose_headers')

    if options.get('max_age'):
        resp.headers[ACL_MAX_AGE] = options.get('max_age')

    if options.get('supports_credentials'):
        resp.headers[ACL_CREDENTIALS] = 'true'

    if request.method == 'OPTIONS':
        resp.headers[ACL_METHODS] = options.get('methods',
                                                _flexible_str(ALL_METHODS))

    # http://www.w3.org/TR/cors/#resource-implementation
    if resp.headers[ACL_ORIGIN] != '*' and options.get('vary_header'):
        vary = ['Origin', resp.headers.get('Vary', None)]
        resp.headers['Vary'] = ', '. join(v for v in vary if v is not None)

def _get_app_kwarg_dict(app=current_app):
    return dict([
                (k.lower().replace('cors_', ''), app.config.get(k))
                for k in CONFIG_OPTIONS
                if app.config.get(k) is not None
                ])

def _try_match(pattern, request_origin):
    '''
        Safely attempts to match a pattern or string to a request origin.

        If a pattern is an illegal regular expression
    '''
    try:
        return re.match(pattern, request_origin)
    except:
        return request_origin == pattern

def _flexible_str(obj):
    if(not isinstance(obj, string_types)
            and isinstance(obj, collections.Iterable)):
        return ', '.join(str(item) for item in sorted(obj))
    else:
        return str(obj)

def _serialize_option(d, key, target_key=None, upper=False):
    if key in d:
        v = _flexible_str(d[key])
        if upper and v:
            v.upper()
        d[target_key or key] = v

def _serialize_options(options):
    '''
        A helper method to serialize and processes the options dictionary
        where applicable.
    '''
    # ensure origins is a list of allowed origins with at least one entry.
    if isinstance(options.get('origins'), string_types):
        options['origins'] = [options.get('origins')]

    _serialize_option(options, 'origins', target_key='origins_str')
    _serialize_option(options, 'methods', upper=True)
    _serialize_option(options, 'headers')
    _serialize_option(options, 'expose_headers')

    if isinstance(options.get('max_age'), timedelta):
        options['max_age'] = str(int(options['max_age'].total_seconds()))
<|endoftext|>