<commit_before>
## Base Exceptions

class HTTPError(Exception):
    "Base exception used by this module."
    pass

class HTTPWarning(Warning):
    "Base warning used by this module."
    pass

class PoolError(HTTPError):
    "Base exception for errors caused within a pool."
    def __init__(self, pool, message):
        self.pool = pool
        HTTPError.__init__(self, "%s: %s" % (pool, message))

    def __reduce__(self):
        # For pickling purposes.
        return self.__class__, (None, None)

class RequestError(PoolError):
    "Base exception for PoolErrors that have associated URLs."
    def __init__(self, pool, url, message):
        self.url = url
        PoolError.__init__(self, pool, message)

    def __reduce__(self):
        # For pickling purposes.
        return self.__class__, (None, self.url, None)

class SSLError(HTTPError):
    "Raised when SSL certificate fails in an HTTPS connection."
    pass

class ProxyError(HTTPError):
    "Raised when the connection to a proxy fails."
    pass

class DecodeError(HTTPError):
    "Raised when automatic decoding based on Content-Type fails."
    pass

class ProtocolError(HTTPError):
    "Raised when something unexpected happens mid-request/response."
    pass

#: Renamed to ProtocolError but aliased for backwards compatibility.
ConnectionError = ProtocolError

## Leaf Exceptions

class MaxRetryError(RequestError):
    """Raised when the maximum number of retries is exceeded.

    :param pool: The connection pool
    :type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool`
    :param string url: The requested Url
    :param exceptions.Exception reason: The underlying error

    """

    def __init__(self, pool, url, reason=None):
        self.reason = reason

        message = "Max retries exceeded with url: %s (Caused by %r)" % (url,
                                                                        reason)

        RequestError.__init__(self, pool, url, message)

class HostChangedError(RequestError):
    "Raised when an existing pool gets a request for a foreign host."

    def __init__(self, pool, url, retries=3):
        message = "Tried to open a foreign host with url: %s" % url
        RequestError.__init__(self, pool, url, message)
        self.retries = retries

class TimeoutStateError(HTTPError):
    """ Raised when passing an invalid state to a timeout """
    pass

class TimeoutError(HTTPError):
    """ Raised when a socket timeout error occurs.

    Catching this error will catch both :exc:`ReadTimeoutErrors
    <ReadTimeoutError>` and :exc:`ConnectTimeoutErrors <ConnectTimeoutError>`.
    """
    pass

class ReadTimeoutError(TimeoutError, RequestError):
    "Raised when a socket timeout occurs while receiving data from a server"
    pass

# This timeout error does not have a URL attached and needs to inherit from the
# base HTTPError
class ConnectTimeoutError(TimeoutError):
    "Raised when a socket timeout occurs while connecting to a server"
    pass

class EmptyPoolError(PoolError):
    "Raised when a pool runs out of connections and no more are allowed."
    pass

class ClosedPoolError(PoolError):
    "Raised when a request enters a pool after the pool has been closed."
    pass

class LocationValueError(ValueError, HTTPError):
    "Raised when there is something wrong with a given URL input."
    pass

class LocationParseError(LocationValueError):
    "Raised when get_host or similar fails to parse the URL input."

    def __init__(self, location):
        message = "Failed to parse: %s" % location
        HTTPError.__init__(self, message)

        self.location = location

class SecurityWarning(HTTPWarning):
    "Warned when perfoming security reducing actions"
    pass

class InsecureRequestWarning(SecurityWarning):
    "Warned when making an unverified HTTPS request."
    pass

class SystemTimeWarning(SecurityWarning):
    "Warned when system time is suspected to be wrong"
    pass
<commit_msg>Reformat<commit_after>
## Base Exceptions

class HTTPError(Exception):
    "Base exception used by this module."
    pass

class HTTPWarning(Warning):
    "Base warning used by this module."
    pass

class PoolError(HTTPError):
    "Base exception for errors caused within a pool."
    def __init__(self, pool, message):
        self.pool = pool
        HTTPError.__init__(self, "%s: %s" % (pool, message))

    def __reduce__(self):
        # For pickling purposes.
        return self.__class__, (None, None)

class RequestError(PoolError):
    "Base exception for PoolErrors that have associated URLs."
    def __init__(self, pool, url, message):
        self.url = url
        PoolError.__init__(self, pool, message)

    def __reduce__(self):
        # For pickling purposes.
        return self.__class__, (None, self.url, None)

class SSLError(HTTPError):
    "Raised when SSL certificate fails in an HTTPS connection."
    pass

class ProxyError(HTTPError):
    "Raised when the connection to a proxy fails."
    pass

class DecodeError(HTTPError):
    "Raised when automatic decoding based on Content-Type fails."
    pass

class ProtocolError(HTTPError):
    "Raised when something unexpected happens mid-request/response."
    pass

#: Renamed to ProtocolError but aliased for backwards compatibility.
ConnectionError = ProtocolError

## Leaf Exceptions

class MaxRetryError(RequestError):
    """Raised when the maximum number of retries is exceeded.

    :param pool: The connection pool
    :type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool`
    :param string url: The requested Url
    :param exceptions.Exception reason: The underlying error

    """

    def __init__(self, pool, url, reason=None):
        self.reason = reason

        message = "Max retries exceeded with url: %s (Caused by %r)" % (
            url, reason)

        RequestError.__init__(self, pool, url, message)

class HostChangedError(RequestError):
    "Raised when an existing pool gets a request for a foreign host."

    def __init__(self, pool, url, retries=3):
        message = "Tried to open a foreign host with url: %s" % url
        RequestError.__init__(self, pool, url, message)
        self.retries = retries

class TimeoutStateError(HTTPError):
    """ Raised when passing an invalid state to a timeout """
    pass

class TimeoutError(HTTPError):
    """ Raised when a socket timeout error occurs.

    Catching this error will catch both :exc:`ReadTimeoutErrors
    <ReadTimeoutError>` and :exc:`ConnectTimeoutErrors <ConnectTimeoutError>`.
    """
    pass

class ReadTimeoutError(TimeoutError, RequestError):
    "Raised when a socket timeout occurs while receiving data from a server"
    pass

# This timeout error does not have a URL attached and needs to inherit from the
# base HTTPError
class ConnectTimeoutError(TimeoutError):
    "Raised when a socket timeout occurs while connecting to a server"
    pass

class EmptyPoolError(PoolError):
    "Raised when a pool runs out of connections and no more are allowed."
    pass

class ClosedPoolError(PoolError):
    "Raised when a request enters a pool after the pool has been closed."
    pass

class LocationValueError(ValueError, HTTPError):
    "Raised when there is something wrong with a given URL input."
    pass

class LocationParseError(LocationValueError):
    "Raised when get_host or similar fails to parse the URL input."

    def __init__(self, location):
        message = "Failed to parse: %s" % location
        HTTPError.__init__(self, message)

        self.location = location

class SecurityWarning(HTTPWarning):
    "Warned when perfoming security reducing actions"
    pass

class InsecureRequestWarning(SecurityWarning):
    "Warned when making an unverified HTTPS request."
    pass

class SystemTimeWarning(SecurityWarning):
    "Warned when system time is suspected to be wrong"
    pass
<|endoftext|>


<commit_before>from __future__ import absolute_import
import time
import logging

from ..exceptions import (
    ConnectTimeoutError,
    MaxRetryError,
    ProtocolError,
    ReadTimeoutError,
    ResponseError,
)
from ..packages import six

log = logging.getLogger(__name__)

class Retry(object):
    """ Retry configuration.

    Each retry attempt will create a new Retry object with updated values, so
    they can be safely reused.

    Retries can be defined as a default for a pool::

        retries = Retry(connect=5, read=2, redirect=5)
        http = PoolManager(retries=retries)
        response = http.request('GET', 'http://example.com/')

    Or per-request (which overrides the default for the pool)::

        response = http.request('GET', 'http://example.com/', retries=Retry(10))

    Retries can be disabled by passing ``False``::

        response = http.request('GET', 'http://example.com/', retries=False)

    Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless
    retries are disabled, in which case the causing exception will be raised.

    :param int total:
        Total number of retries to allow. Takes precedence over other counts.

        Set to ``None`` to remove this constraint and fall back on other
        counts. It's a good idea to set this to some sensibly-high value to
        account for unexpected edge cases and avoid infinite retry loops.

        Set to ``0`` to fail on the first retry.

        Set to ``False`` to disable and imply ``raise_on_redirect=False``.

    :param int connect:
        How many connection-related errors to retry on.

        These are errors raised before the request is sent to the remote server,
        which we assume has not triggered the server to process the request.

        Set to ``0`` to fail on the first retry of this type.

    :param int read:
        How many times to retry on read errors.

        These errors are raised after the request was sent to the server, so the
        request may have side-effects.

        Set to ``0`` to fail on the first retry of this type.

    :param int redirect:
        How many redirects to perform. Limit this to avoid infinite redirect
        loops.

        A redirect is a HTTP response with a status code 301, 302, 303, 307 or
        308.

        Set to ``0`` to fail on the first retry of this type.

        Set to ``False`` to disable and imply ``raise_on_redirect=False``.

    :param iterable method_whitelist:
        Set of uppercased HTTP method verbs that we should retry on.

        By default, we only retry on methods which are considered to be
        indempotent (multiple requests with the same parameters end with the
        same state). See :attr:`Retry.DEFAULT_METHOD_WHITELIST`.

    :param iterable status_forcelist:
        A set of HTTP status codes that we should force a retry on.

        By default, this is disabled with ``None``.

    :param float backoff_factor:
        A backoff factor to apply between attempts. urllib3 will sleep for::

            {backoff factor} * (2 ^ ({number of total retries} - 1))

        seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep
        for [0.1s, 0.2s, 0.4s, ...] between retries. It will never be longer
        than :attr:`Retry.BACKOFF_MAX`.

        By default, backoff is disabled (set to 0).

    :param bool raise_on_redirect: Whether, if the number of redirects is
        exhausted, to raise a MaxRetryError, or to return a response with a
        response code in the 3xx range.

    :param bool raise_on_status: Similar meaning to ``raise_on_redirect``:
        whether we should raise an exception, or return a response,
        if status falls in ``status_forcelist`` range and retries have
        been exhausted.
    """

    DEFAULT_METHOD_WHITELIST = frozenset([
        'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'])

    #: Maximum backoff time.
    BACKOFF_MAX = 120

    def __init__(self, total=10, connect=None, read=None, redirect=None,
                 method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None,
                 backoff_factor=0, raise_on_redirect=True, raise_on_status=True,
                 history=None):

        self.total = total
        self.connect = connect
        self.read = read

        if redirect is False or total is False:
            redirect = 0
            raise_on_redirect = False

        self.redirect = redirect
        self.status_forcelist = status_forcelist or set()
        self.method_whitelist = method_whitelist
        self.backoff_factor = backoff_factor
        self.raise_on_redirect = raise_on_redirect
        self.raise_on_status = raise_on_status
        self.history = history or []

    def new(self, **kw):
        params = dict(
            total=self.total,
            connect=self.connect, read=self.read, redirect=self.redirect,
            method_whitelist=self.method_whitelist,
            status_forcelist=self.status_forcelist,
            backoff_factor=self.backoff_factor,
            raise_on_redirect=self.raise_on_redirect,
            raise_on_status=self.raise_on_status,
            history=self.history,
        )
        params.update(kw)
        return type(self)(**params)

    @classmethod
    def from_int(cls, retries, redirect=True, default=None):
        """ Backwards-compatibility for the old retries format."""
        if retries is None:
            retries = default if default is not None else cls.DEFAULT

        if isinstance(retries, Retry):
            return retries

        redirect = bool(redirect) and None
        new_retries = cls(retries, redirect=redirect)
        log.debug("Converted retries value: %r -> %r", retries, new_retries)
        return new_retries

    def get_backoff_time(self):
        """ Formula for computing the current backoff

        :rtype: float
        """
        if len(self.history) <= 1:
            return 0

        backoff_value = self.backoff_factor * (2 ** (len(self.history) - 1))
        return min(self.BACKOFF_MAX, backoff_value)

    def sleep(self):
        """ Sleep between retry attempts using an exponential backoff.

        By default, the backoff factor is 0 and this method will return
        immediately.
        """
        backoff = self.get_backoff_time()
        if backoff <= 0:
            return
        time.sleep(backoff)

    def _is_connection_error(self, err):
        """ Errors when we're fairly sure that the server did not receive the
        request, so it should be safe to retry.
        """
        return isinstance(err, ConnectTimeoutError)

    def _is_read_error(self, err):
        """ Errors that occur after the request has been started, so we should
        assume that the server began processing it.
        """
        return isinstance(err, (ReadTimeoutError, ProtocolError))

    def is_forced_retry(self, method, status_code):
        """ Is this method/status code retryable? (Based on method/codes whitelists)
        """
        if self.method_whitelist and method.upper() not in self.method_whitelist:
            return False

        return self.status_forcelist and status_code in self.status_forcelist

    def is_exhausted(self):
        """ Are we out of retries? """
        retry_counts = (self.total, self.connect, self.read, self.redirect)
        retry_counts = list(filter(None, retry_counts))
        if not retry_counts:
            return False

        return min(retry_counts) < 0

    def increment(self, method=None, url=None, response=None, error=None,
                  _pool=None, _stacktrace=None):
        """ Return a new Retry object with incremented retry counters.

        :param response: A response object, or None, if the server did not
            return a response.
        :type response: :class:`~urllib3.response.HTTPResponse`
        :param Exception error: An error encountered during the request, or
            None if the response was received successfully.

        :return: A new ``Retry`` object.
        """
        if self.total is False and error:
            # Disabled, indicate to re-raise the error.
            raise six.reraise(type(error), error, _stacktrace)

        total = self.total
        if total is not None:
            total -= 1

        history = self.history
        connect = self.connect
        read = self.read
        redirect = self.redirect
        cause = 'unknown'

        if error and self._is_connection_error(error):
            # Connect retry?
            if connect is False:
                raise six.reraise(type(error), error, _stacktrace)
            elif connect is not None:
                connect -= 1
            history.append(error)

        elif error and self._is_read_error(error):
            # Read retry?
            if read is False:
                raise six.reraise(type(error), error, _stacktrace)
            elif read is not None:
                read -= 1
            history.append(error)

        elif response and response.get_redirect_location():
            # Redirect retry?
            if redirect is not None:
                redirect -= 1
            cause = 'too many redirects'

        else:
            # Incrementing because of a server error like a 500 in
            # status_forcelist and a the given method is in the whitelist
            cause = ResponseError.GENERIC_ERROR
            if response and response.status:
                cause = ResponseError.SPECIFIC_ERROR.format(
                    status_code=response.status)
            history.append(ResponseError(cause))

        new_retry = self.new(
            total=total,
            connect=connect, read=read, redirect=redirect,
            history=history)

        if new_retry.is_exhausted():
            raise MaxRetryError(_pool, url, error or ResponseError(cause))

        log.debug("Incremented Retry for (url='%s'): %r", url, new_retry)

        return new_retry

    def __repr__(self):
        return ('{cls.__name__}(total={self.total}, connect={self.connect}, '
                'read={self.read}, redirect={self.redirect})').format(
                    cls=type(self), self=self)

# For backwards compatibility (equivalent to pre-v1.9):
Retry.DEFAULT = Retry(3)
<commit_msg>Added documentation to Retry history attribute<commit_after>from __future__ import absolute_import
import time
import logging

from ..exceptions import (
    ConnectTimeoutError,
    MaxRetryError,
    ProtocolError,
    ReadTimeoutError,
    ResponseError,
)
from ..packages import six

log = logging.getLogger(__name__)

class Retry(object):
    """ Retry configuration.

    Each retry attempt will create a new Retry object with updated values, so
    they can be safely reused.

    Retries can be defined as a default for a pool::

        retries = Retry(connect=5, read=2, redirect=5)
        http = PoolManager(retries=retries)
        response = http.request('GET', 'http://example.com/')

    Or per-request (which overrides the default for the pool)::

        response = http.request('GET', 'http://example.com/', retries=Retry(10))

    Retries can be disabled by passing ``False``::

        response = http.request('GET', 'http://example.com/', retries=False)

    Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless
    retries are disabled, in which case the causing exception will be raised.

    :param int total:
        Total number of retries to allow. Takes precedence over other counts.

        Set to ``None`` to remove this constraint and fall back on other
        counts. It's a good idea to set this to some sensibly-high value to
        account for unexpected edge cases and avoid infinite retry loops.

        Set to ``0`` to fail on the first retry.

        Set to ``False`` to disable and imply ``raise_on_redirect=False``.

    :param int connect:
        How many connection-related errors to retry on.

        These are errors raised before the request is sent to the remote server,
        which we assume has not triggered the server to process the request.

        Set to ``0`` to fail on the first retry of this type.

    :param int read:
        How many times to retry on read errors.

        These errors are raised after the request was sent to the server, so the
        request may have side-effects.

        Set to ``0`` to fail on the first retry of this type.

    :param int redirect:
        How many redirects to perform. Limit this to avoid infinite redirect
        loops.

        A redirect is a HTTP response with a status code 301, 302, 303, 307 or
        308.

        Set to ``0`` to fail on the first retry of this type.

        Set to ``False`` to disable and imply ``raise_on_redirect=False``.

    :param iterable method_whitelist:
        Set of uppercased HTTP method verbs that we should retry on.

        By default, we only retry on methods which are considered to be
        indempotent (multiple requests with the same parameters end with the
        same state). See :attr:`Retry.DEFAULT_METHOD_WHITELIST`.

    :param iterable status_forcelist:
        A set of HTTP status codes that we should force a retry on.

        By default, this is disabled with ``None``.

    :param float backoff_factor:
        A backoff factor to apply between attempts. urllib3 will sleep for::

            {backoff factor} * (2 ^ ({number of total retries} - 1))

        seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep
        for [0.1s, 0.2s, 0.4s, ...] between retries. It will never be longer
        than :attr:`Retry.BACKOFF_MAX`.

        By default, backoff is disabled (set to 0).

    :param bool raise_on_redirect: Whether, if the number of redirects is
        exhausted, to raise a MaxRetryError, or to return a response with a
        response code in the 3xx range.

    :param bool raise_on_status: Similar meaning to ``raise_on_redirect``:
        whether we should raise an exception, or return a response,
        if status falls in ``status_forcelist`` range and retries have
        been exhausted.

    :param list history: The history of the errors encountered during
        each call to :meth:`~Retry.increment`. The list is in the order
        the errors occurred.
    """

    DEFAULT_METHOD_WHITELIST = frozenset([
        'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'])

    #: Maximum backoff time.
    BACKOFF_MAX = 120

    def __init__(self, total=10, connect=None, read=None, redirect=None,
                 method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None,
                 backoff_factor=0, raise_on_redirect=True, raise_on_status=True,
                 history=None):

        self.total = total
        self.connect = connect
        self.read = read

        if redirect is False or total is False:
            redirect = 0
            raise_on_redirect = False

        self.redirect = redirect
        self.status_forcelist = status_forcelist or set()
        self.method_whitelist = method_whitelist
        self.backoff_factor = backoff_factor
        self.raise_on_redirect = raise_on_redirect
        self.raise_on_status = raise_on_status
        self.history = history or []

    def new(self, **kw):
        params = dict(
            total=self.total,
            connect=self.connect, read=self.read, redirect=self.redirect,
            method_whitelist=self.method_whitelist,
            status_forcelist=self.status_forcelist,
            backoff_factor=self.backoff_factor,
            raise_on_redirect=self.raise_on_redirect,
            raise_on_status=self.raise_on_status,
            history=self.history,
        )
        params.update(kw)
        return type(self)(**params)

    @classmethod
    def from_int(cls, retries, redirect=True, default=None):
        """ Backwards-compatibility for the old retries format."""
        if retries is None:
            retries = default if default is not None else cls.DEFAULT

        if isinstance(retries, Retry):
            return retries

        redirect = bool(redirect) and None
        new_retries = cls(retries, redirect=redirect)
        log.debug("Converted retries value: %r -> %r", retries, new_retries)
        return new_retries

    def get_backoff_time(self):
        """ Formula for computing the current backoff

        :rtype: float
        """
        if len(self.history) <= 1:
            return 0

        backoff_value = self.backoff_factor * (2 ** (len(self.history) - 1))
        return min(self.BACKOFF_MAX, backoff_value)

    def sleep(self):
        """ Sleep between retry attempts using an exponential backoff.

        By default, the backoff factor is 0 and this method will return
        immediately.
        """
        backoff = self.get_backoff_time()
        if backoff <= 0:
            return
        time.sleep(backoff)

    def _is_connection_error(self, err):
        """ Errors when we're fairly sure that the server did not receive the
        request, so it should be safe to retry.
        """
        return isinstance(err, ConnectTimeoutError)

    def _is_read_error(self, err):
        """ Errors that occur after the request has been started, so we should
        assume that the server began processing it.
        """
        return isinstance(err, (ReadTimeoutError, ProtocolError))

    def is_forced_retry(self, method, status_code):
        """ Is this method/status code retryable? (Based on method/codes whitelists)
        """
        if self.method_whitelist and method.upper() not in self.method_whitelist:
            return False

        return self.status_forcelist and status_code in self.status_forcelist

    def is_exhausted(self):
        """ Are we out of retries? """
        retry_counts = (self.total, self.connect, self.read, self.redirect)
        retry_counts = list(filter(None, retry_counts))
        if not retry_counts:
            return False

        return min(retry_counts) < 0

    def increment(self, method=None, url=None, response=None, error=None,
                  _pool=None, _stacktrace=None):
        """ Return a new Retry object with incremented retry counters.

        :param response: A response object, or None, if the server did not
            return a response.
        :type response: :class:`~urllib3.response.HTTPResponse`
        :param Exception error: An error encountered during the request, or
            None if the response was received successfully.

        :return: A new ``Retry`` object.
        """
        if self.total is False and error:
            # Disabled, indicate to re-raise the error.
            raise six.reraise(type(error), error, _stacktrace)

        total = self.total
        if total is not None:
            total -= 1

        history = self.history
        connect = self.connect
        read = self.read
        redirect = self.redirect
        cause = 'unknown'

        if error and self._is_connection_error(error):
            # Connect retry?
            if connect is False:
                raise six.reraise(type(error), error, _stacktrace)
            elif connect is not None:
                connect -= 1
            history.append(error)

        elif error and self._is_read_error(error):
            # Read retry?
            if read is False:
                raise six.reraise(type(error), error, _stacktrace)
            elif read is not None:
                read -= 1
            history.append(error)

        elif response and response.get_redirect_location():
            # Redirect retry?
            if redirect is not None:
                redirect -= 1
            cause = 'too many redirects'

        else:
            # Incrementing because of a server error like a 500 in
            # status_forcelist and a the given method is in the whitelist
            cause = ResponseError.GENERIC_ERROR
            if response and response.status:
                cause = ResponseError.SPECIFIC_ERROR.format(
                    status_code=response.status)
            history.append(ResponseError(cause))

        new_retry = self.new(
            total=total,
            connect=connect, read=read, redirect=redirect,
            history=history)

        if new_retry.is_exhausted():
            raise MaxRetryError(_pool, url, error or ResponseError(cause))

        log.debug("Incremented Retry for (url='%s'): %r", url, new_retry)

        return new_retry

    def __repr__(self):
        return ('{cls.__name__}(total={self.total}, connect={self.connect}, '
                'read={self.read}, redirect={self.redirect})').format(
                    cls=type(self), self=self)

# For backwards compatibility (equivalent to pre-v1.9):
Retry.DEFAULT = Retry(3)
<|endoftext|>


<commit_before>from __future__ import absolute_import, division, print_function, unicode_literals

from collections import namedtuple

from grit import Git
from grit import Settings
from grit.command import Open

HELP = """
git p[ulls] [s[hort]]
    List all the outstanding pull requests for the current project.

    If the optional "short" argument is passed in, fit each pull into one line.
"""

_PULL_URL = 'https://github.com/{project_user}/{project}/pulls'
_PULL_HREF = '/{project_user}/{project}/pull/'

SAFE = True

_FORMATS = '#%d. %s\n    %s\n', '#%s. %-24s  %s'

def _to_string(pull, short):
    number, branch, title = pull['number'], pull['head']['label'], pull['title']
    return _FORMATS[bool(short)] % (number, branch, title)

def _raw_pulls():
    return Git.api('repos', Settings.PROJECT_USER, Settings.PROJECT, 'pulls')

def _pull_urls():
    settings = {'project': Settings.PROJECT,
                'project_user': Settings.PROJECT_USER}
    return _PULL_URL.format(**settings), _PULL_HREF.format(**settings)

def open_pulls():
    url, _ = _pull_urls()
    Open.open_url(url)

def pulls(arg=''):
    short = arg and 'short'.startswith(arg)
    if arg and not short:
        raise ValueError("Can't understand pull argument '%s'" % arg)

    for p in _raw_pulls():
        print(_to_string(p, short))
 #
<commit_msg>Simplify pull calculations slightly.<commit_after>from __future__ import absolute_import, division, print_function, unicode_literals

from collections import namedtuple

from grit import Git
from grit import Settings
from grit.command import Open

HELP = """
git p[ulls] [s[hort]]
    List all the outstanding pull requests for the current project.

    If the optional "short" argument is passed in, fit each pull into one line.
"""

_PULL_URL = 'https://github.com/{project_user}/{project}/pulls'
_PULL_HREF = '/{project_user}/{project}/pull/'

SAFE = True

_FORMATS = '#%d. %s\n    %s\n', '#%s. %-24s  %s'

def _to_string(pull, short):
    return _FORMATS[bool(short)] % pull

def _raw_pulls():
    for p in Git.api('repos', Settings.PROJECT_USER, Settings.PROJECT, 'pulls'):
        yield p['number'], p['head']['label'], p['title']

def _pull_urls():
    settings = {'project': Settings.PROJECT,
                'project_user': Settings.PROJECT_USER}
    return _PULL_URL.format(**settings), _PULL_HREF.format(**settings)

def open_pulls():
    url, _ = _pull_urls()
    Open.open_url(url)

def pulls(arg=''):
    short = arg and 'short'.startswith(arg)
    if arg and not short:
        raise ValueError("Can't understand pull argument '%s'" % arg)

    for p in _raw_pulls():
        print(_to_string(p, short))
 #
<|endoftext|>


<commit_before>from datetime import datetime
import json
from unittest import TestCase
from urllib.parse import urlparse

from usb import create_application
from usb.models import db, Redirect, DeviceType

class APITestCase(TestCase):

    def setUp(self):
        self.app = create_application('config/test.py')
        self.client = self.app.test_client()

        db.app = self.app
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()

    def _post(self, url, data):
        return self.client.post(url, data=json.dumps(data), content_type='application/json')

    def _patch(self, url, data):
        return self.client.patch(url, data=json.dumps(data), content_type='application/json')

    def assertShortIdFormat(self, short_id):
        self.assertRegex(
            short_id,
            r'^[a-zA-Z0-9]{{{min_length},}}$'.format(
                min_length=self.app.config['SHORT_URL_MIN_LENGTH']
            )
        )

    def assertShortURL(self, short_url):
        url = urlparse(short_url)
        self.assertEqual(url.scheme, 'http')
        self.assertEqual(url.netloc, self.app.config['SHORT_URL_DOMAIN'])
        self.assertEqual(url.path.count('/'), 1)
        self.assertFalse(url.params)
        self.assertFalse(url.query)
        self.assertFalse(url.fragment)

    def test_redirect_from_index_namespace(self):
        pass

    def test_redirect_from_links_namespace(self):
        pass

    def test_create_short_link(self):
        long_url = 'https://www.youtube.com/watch?v=Y21VecIIdBI'
        response = self._post('/links', {'url': long_url})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers['Content-Type'], 'application/json')
        data = json.loads(response.data)
        self.assertIn('url', data)
        short_url = data['url']
        self.assertNotEqual(long_url, short_url)
        self.assertShortURL(short_url)
        short_id = short_url.split('/')[-1]
        self.assertShortIdFormat(short_id)

    def test_create_short_link_for_already_shortened(self):
        long_url = 'https://www.youtube.com/watch?v=Y21VecIIdBI'
        response = self._post('/links', {'url': long_url})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers['Content-Type'], 'application/json')
        data_from_first_call = json.loads(response.data)
        self.assertIn('url', data_from_first_call)
        short_url = data_from_first_call['url']
        self.assertNotEqual(long_url, short_url)
        self.assertShortURL(short_url)
        short_id = short_url.split('/')[-1]
        self.assertShortIdFormat(short_id)
        response = self._post('/links', {'url': long_url})
        self.assertEqual(response.status_code, 409)
        self.assertEqual(response.headers['Content-Type'], 'application/json')
        data_from_second_call = json.loads(response.data)
        self.assertEqual(data_from_first_call, data_from_second_call)

    def test_update_short_link(self):
        dt = datetime.now()
        redirects = [
            Redirect('aaaaaaaa', DeviceType.DESKTOP, 'http://domain1.com/path?q=a', count=10, datetime=dt),
            Redirect('aaaaaaaa', DeviceType.TABLET, 'http://tablet.domain1.com/path?q=a', count=20, datetime=dt),
            Redirect('aaaaaaaa', DeviceType.MOBILE, 'http://mobile.domain1.com/path?q=a', count=30, datetime=dt)
        ]
        for redirect in redirects:
            db.session.add(redirect)
        db.session.commit()
        self._patch('/links/aaaaaaaa', {'tablet': 'http://gov.us/elect/president?name='})
        tablet_redirects = Redirect.query.filter_by(short='aaaaaaaa', type=DeviceType.TABLET).all()
        self.assertTrue(tablet_redirects)
        self.assertEqual(len(tablet_redirects), 1)
        tablet_redirect = tablet_redirects[0]
        self.assertEqual(tablet_redirect.short, 'aaaaaaaa')
        self.assertEqual(tablet_redirect.type, DeviceType.TABLET)
        self.assertEqual(tablet_redirect.url, 'http://gov.us/elect/president?name=')

    def test_get_list_of_short_links(self):
        dt = datetime.now()
        dt_str = dt.isoformat()
        redirects = [
            Redirect('aaaaaaaa', DeviceType.DESKTOP, 'http://domain1.com/path?q=a', count=10, datetime=dt),
            Redirect('aaaaaaaa', DeviceType.TABLET, 'http://tablet.domain1.com/path?q=a', count=20, datetime=dt),
            Redirect('aaaaaaaa', DeviceType.MOBILE, 'http://mobile.domain1.com/path?q=a', count=30, datetime=dt),
            Redirect('bbbbbbbb', DeviceType.DESKTOP, 'http://domain2.com/path/b', count=40, datetime=dt),
            Redirect('bbbbbbbb', DeviceType.TABLET, 'http://tablet.domain2.com/path/b', count=5, datetime=dt),
            Redirect('bbbbbbbb', DeviceType.MOBILE, 'http://mobile.domain2.com/path/b', count=15, datetime=dt),
            Redirect('cccccccc', DeviceType.DESKTOP, 'http://domain1.com/path?q=a', count=1, datetime=dt),
            Redirect('cccccccc', DeviceType.TABLET, 'http://domain1.com/path?q=a', count=2, datetime=dt),
            Redirect('cccccccc', DeviceType.MOBILE, 'http://domain1.com/path?q=a', count=3, datetime=dt)
        ]
        for redirect in redirects:
            db.session.add(redirect)
        db.session.commit()
        response = self.client.get('/links')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers['Content-Type'], 'application/json')
        data = json.loads(response.data)
        self.assertEqual(data, {
            'aaaaaaaa': [
                {'type': 'desktop', 'url': 'http://domain1.com/path?q=a', 'redirects': 10, 'datetime': dt_str},
                {'type': 'tablet', 'url': 'http://tablet.domain1.com/path?q=a', 'redirects': 20, 'datetime': dt_str},
                {'type': 'mobile', 'url': 'http://mobile.domain1.com/path?q=a', 'redirects': 30, 'datetime': dt_str}
            ],
            'bbbbbbbb': [
                {'type': 'desktop', 'url': 'http://domain2.com/path/b', 'redirects': 40, 'datetime': dt_str},
                {'type': 'tablet', 'url': 'http://tablet.domain2.com/path/b', 'redirects': 5, 'datetime': dt_str},
                {'type': 'mobile', 'url': 'http://mobile.domain2.com/path/b', 'redirects': 15, 'datetime': dt_str}
            ],
            'cccccccc': [
                {'type': 'desktop', 'url': 'http://domain1.com/path?q=a', 'redirects': 1, 'datetime': dt_str},
                {'type': 'tablet', 'url': 'http://domain1.com/path?q=a', 'redirects': 2, 'datetime': dt_str},
                {'type': 'mobile', 'url': 'http://domain1.com/path?q=a', 'redirects': 3, 'datetime': dt_str}
            ]
        })

    def test_get_list_of_short_links_empty_db(self):
        response = self.client.get('/links')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers['Content-Type'], 'application/json')
        data = json.loads(response.data)
        self.assertEqual(data, {})
<commit_msg>Add test for update a nonexistent short URL<commit_after>from datetime import datetime
import json
from unittest import TestCase
from urllib.parse import urlparse

from usb import create_application
from usb.models import db, Redirect, DeviceType

class APITestCase(TestCase):

    def setUp(self):
        self.app = create_application('config/test.py')
        self.client = self.app.test_client()

        db.app = self.app
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()

    def _post(self, url, data):
        return self.client.post(url, data=json.dumps(data), content_type='application/json')

    def _patch(self, url, data):
        return self.client.patch(url, data=json.dumps(data), content_type='application/json')

    def assertShortIdFormat(self, short_id):
        self.assertRegex(
            short_id,
            r'^[a-zA-Z0-9]{{{min_length},}}$'.format(
                min_length=self.app.config['SHORT_URL_MIN_LENGTH']
            )
        )

    def assertShortURL(self, short_url):
        url = urlparse(short_url)
        self.assertEqual(url.scheme, 'http')
        self.assertEqual(url.netloc, self.app.config['SHORT_URL_DOMAIN'])
        self.assertEqual(url.path.count('/'), 1)
        self.assertFalse(url.params)
        self.assertFalse(url.query)
        self.assertFalse(url.fragment)

    def test_redirect_from_index_namespace(self):
        pass

    def test_redirect_from_links_namespace(self):
        pass

    def test_create_short_link(self):
        long_url = 'https://www.youtube.com/watch?v=Y21VecIIdBI'
        response = self._post('/links', {'url': long_url})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers['Content-Type'], 'application/json')
        data = json.loads(response.data)
        self.assertIn('url', data)
        short_url = data['url']
        self.assertNotEqual(long_url, short_url)
        self.assertShortURL(short_url)
        short_id = short_url.split('/')[-1]
        self.assertShortIdFormat(short_id)

    def test_create_short_link_for_already_shortened(self):
        long_url = 'https://www.youtube.com/watch?v=Y21VecIIdBI'
        response = self._post('/links', {'url': long_url})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers['Content-Type'], 'application/json')
        data_from_first_call = json.loads(response.data)
        self.assertIn('url', data_from_first_call)
        short_url = data_from_first_call['url']
        self.assertNotEqual(long_url, short_url)
        self.assertShortURL(short_url)
        short_id = short_url.split('/')[-1]
        self.assertShortIdFormat(short_id)
        response = self._post('/links', {'url': long_url})
        self.assertEqual(response.status_code, 409)
        self.assertEqual(response.headers['Content-Type'], 'application/json')
        data_from_second_call = json.loads(response.data)
        self.assertEqual(data_from_first_call, data_from_second_call)

    def test_update_short_link(self):
        dt = datetime.now()
        redirects = [
            Redirect('aaaaaaaa', DeviceType.DESKTOP, 'http://domain1.com/path?q=a', count=10, datetime=dt),
            Redirect('aaaaaaaa', DeviceType.TABLET, 'http://tablet.domain1.com/path?q=a', count=20, datetime=dt),
            Redirect('aaaaaaaa', DeviceType.MOBILE, 'http://mobile.domain1.com/path?q=a', count=30, datetime=dt)
        ]
        for redirect in redirects:
            db.session.add(redirect)
        db.session.commit()
        self._patch('/links/aaaaaaaa', {'tablet': 'http://gov.us/elect/president?name='})
        tablet_redirects = Redirect.query.filter_by(short='aaaaaaaa', type=DeviceType.TABLET).all()
        self.assertTrue(tablet_redirects)
        self.assertEqual(len(tablet_redirects), 1)
        tablet_redirect = tablet_redirects[0]
        self.assertEqual(tablet_redirect.short, 'aaaaaaaa')
        self.assertEqual(tablet_redirect.type, DeviceType.TABLET)
        self.assertEqual(tablet_redirect.url, 'http://gov.us/elect/president?name=')

    def test_update_short_link_no_such_short_id(self):
        dt = datetime.now()
        redirects = [
            Redirect('aaaaaaaa', DeviceType.DESKTOP, 'http://domain1.com/path?q=a', count=10, datetime=dt),
            Redirect('aaaaaaaa', DeviceType.TABLET, 'http://tablet.domain1.com/path?q=a', count=20, datetime=dt),
            Redirect('aaaaaaaa', DeviceType.MOBILE, 'http://mobile.domain1.com/path?q=a', count=30, datetime=dt)
        ]
        for redirect in redirects:
            db.session.add(redirect)
        db.session.commit()
        response = self._patch('/links/bbb', {'tablet': 'http://gov.us/elect/president?name='})
        self.assertEqual(response.status_code, 404)
        self.assertEqual(response.headers['Content-Type'], 'application/json')
        data = json.loads(response.data)
        self.assertEqual(data, {})

    def test_get_list_of_short_links(self):
        dt = datetime.now()
        dt_str = dt.isoformat()
        redirects = [
            Redirect('aaaaaaaa', DeviceType.DESKTOP, 'http://domain1.com/path?q=a', count=10, datetime=dt),
            Redirect('aaaaaaaa', DeviceType.TABLET, 'http://tablet.domain1.com/path?q=a', count=20, datetime=dt),
            Redirect('aaaaaaaa', DeviceType.MOBILE, 'http://mobile.domain1.com/path?q=a', count=30, datetime=dt),
            Redirect('bbbbbbbb', DeviceType.DESKTOP, 'http://domain2.com/path/b', count=40, datetime=dt),
            Redirect('bbbbbbbb', DeviceType.TABLET, 'http://tablet.domain2.com/path/b', count=5, datetime=dt),
            Redirect('bbbbbbbb', DeviceType.MOBILE, 'http://mobile.domain2.com/path/b', count=15, datetime=dt),
            Redirect('cccccccc', DeviceType.DESKTOP, 'http://domain1.com/path?q=a', count=1, datetime=dt),
            Redirect('cccccccc', DeviceType.TABLET, 'http://domain1.com/path?q=a', count=2, datetime=dt),
            Redirect('cccccccc', DeviceType.MOBILE, 'http://domain1.com/path?q=a', count=3, datetime=dt)
        ]
        for redirect in redirects:
            db.session.add(redirect)
        db.session.commit()
        response = self.client.get('/links')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers['Content-Type'], 'application/json')
        data = json.loads(response.data)
        self.assertEqual(data, {
            'aaaaaaaa': [
                {'type': 'desktop', 'url': 'http://domain1.com/path?q=a', 'redirects': 10, 'datetime': dt_str},
                {'type': 'tablet', 'url': 'http://tablet.domain1.com/path?q=a', 'redirects': 20, 'datetime': dt_str},
                {'type': 'mobile', 'url': 'http://mobile.domain1.com/path?q=a', 'redirects': 30, 'datetime': dt_str}
            ],
            'bbbbbbbb': [
                {'type': 'desktop', 'url': 'http://domain2.com/path/b', 'redirects': 40, 'datetime': dt_str},
                {'type': 'tablet', 'url': 'http://tablet.domain2.com/path/b', 'redirects': 5, 'datetime': dt_str},
                {'type': 'mobile', 'url': 'http://mobile.domain2.com/path/b', 'redirects': 15, 'datetime': dt_str}
            ],
            'cccccccc': [
                {'type': 'desktop', 'url': 'http://domain1.com/path?q=a', 'redirects': 1, 'datetime': dt_str},
                {'type': 'tablet', 'url': 'http://domain1.com/path?q=a', 'redirects': 2, 'datetime': dt_str},
                {'type': 'mobile', 'url': 'http://domain1.com/path?q=a', 'redirects': 3, 'datetime': dt_str}
            ]
        })

    def test_get_list_of_short_links_empty_db(self):
        response = self.client.get('/links')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers['Content-Type'], 'application/json')
        data = json.loads(response.data)
        self.assertEqual(data, {})
<|endoftext|>


<commit_before>#!/usr/bin/env python
#
# Copyright (c) 2014-2015 ARM Limited
# All rights reserved
#
# The license below extends only to copyright in the software and shall
# not be construed as granting a license to any other intellectual
# property including but not limited to intellectual property relating
# to a hardware implementation of the functionality of the software
# licensed hereunder.  You may use the software subject to the license
# terms below provided that you ensure that this notice is replicated
# unmodified and in its entirety in all distributions of the software,
# modified or unmodified, in source code or in binary form.
#
# Copyright (c) 2011 The Hewlett-Packard Development Company
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met: redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer;
# 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;
# neither the name of the copyright holders nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# 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.
#
# Authors: Nathan Binkert
#          Andreas Sandberg

import os
import re
import sys

from file_types import *

cpp_c_headers = {
    'assert.h' : 'cassert',
    'ctype.h'  : 'cctype',
    'errno.h'  : 'cerrno',
    'float.h'  : 'cfloat',
    'limits.h' : 'climits',
    'locale.h' : 'clocale',
    'math.h'   : 'cmath',
    'setjmp.h' : 'csetjmp',
    'signal.h' : 'csignal',
    'stdarg.h' : 'cstdarg',
    'stddef.h' : 'cstddef',
    'stdio.h'  : 'cstdio',
    'stdlib.h' : 'cstdlib',
    'string.h' : 'cstring',
    'time.h'   : 'ctime',
    'wchar.h'  : 'cwchar',
    'wctype.h' : 'cwctype',
}

include_re = re.compile(r'([#%])(include|import).*[<"](.*)[">]')
def include_key(line):
    '''Mark directories with a leading space so directories
    are sorted before files'''

    match = include_re.match(line)
    assert match, line
    keyword = match.group(2)
    include = match.group(3)

    # Everything but the file part needs to have a space prepended
    parts = include.split('/')
    if len(parts) == 2 and parts[0] == 'dnet':
        # Don't sort the dnet includes with respect to each other, but
        # make them sorted with respect to non dnet includes.  Python
        # guarantees that sorting is stable, so just clear the
        # basename part of the filename.
        parts[1] = ' '
    parts[0:-1] = [ ' ' + s for s in parts[0:-1] ]
    key = '/'.join(parts)

    return key

def _include_matcher(keyword="#include", delim="<>"):
    """Match an include statement and return a (keyword, file, extra)
    duple, or a touple of None values if there isn't a match."""

    rex = re.compile(r'^(%s)\s*%s(.*)%s(.*)$' % (keyword, delim[0], delim[1]))

    def matcher(context, line):
        m = rex.match(line)
        return m.groups() if m else (None, ) * 3

    return matcher

def _include_matcher_fname(fname, **kwargs):
    """Match an include of a specific file name. Any keyword arguments
    are forwarded to _include_matcher, which is used to match the
    actual include line."""

    rex = re.compile(fname)
    base_matcher = _include_matcher(**kwargs)

    def matcher(context, line):
        (keyword, fname, extra) = base_matcher(context, line)
        if fname and rex.match(fname):
            return (keyword, fname, extra)
        else:
            return (None, ) * 3

    return matcher

def _include_matcher_main():
    """Match a C/C++ source file's primary header (i.e., a file with
    the same base name, but a header extension)."""

    base_matcher = _include_matcher(delim='""')
    rex = re.compile(r"^src/(.*)\.([^.]+)$")
    header_map = {
        "c" : "h",
        "cc" : "hh",
        "cpp" : "hh",
        }
    def matcher(context, line):
        m = rex.match(context["filename"])
        if not m:
            return (None, ) * 3
        base, ext = m.groups()
        (keyword, fname, extra) = base_matcher(context, line)
        try:
            if fname == "%s.%s" % (base, header_map[ext]):
                return (keyword, fname, extra)
        except KeyError:
            pass

        return (None, ) * 3

    return matcher

class SortIncludes(object):
    # different types of includes for different sorting of headers
    # <Python.h>         - Python header needs to be first if it exists
    # <*.h>              - system headers (directories before files)
    # <*>                - STL headers
    # <*.(hh|hxx|hpp|H)> - C++ Headers (directories before files)
    # "*"                - M5 headers (directories before files)
    includes_re = (
        ('main', '""', _include_matcher_main()),
        ('python', '<>', _include_matcher_fname("^Python\.h$")),
        ('c', '<>', _include_matcher_fname("^.*\.h$")),
        ('stl', '<>', _include_matcher_fname("^\w+$")),
        ('cc', '<>', _include_matcher_fname("^.*\.(hh|hxx|hpp|H)$")),
        ('m5header', '""', _include_matcher_fname("^.*\.h{1,2}$", delim='""')),
        ('swig0', '<>', _include_matcher(keyword="%import")),
        ('swig1', '<>', _include_matcher(keyword="%include")),
        ('swig2', '""', _include_matcher(keyword="%import", delim='""')),
        ('swig3', '""', _include_matcher(keyword="%include", delim='""')),
        )

    block_order = (
        ('main', ),
        ('python', ),
        ('c', ),
        ('stl', ),
        ('cc', ),
        ('m5header', ),
        ('swig0', 'swig1', 'swig2', 'swig3', ),
        )

    def __init__(self):
        self.block_priority = {}
        for prio, keys in enumerate(self.block_order):
            for key in keys:
                self.block_priority[key] = prio

    def reset(self):
        # clear all stored headers
        self.includes = {}

    def dump_blocks(self, block_types):
        """Merge includes of from several block types into one large
        block of sorted includes. This is useful when we have multiple
        include block types (e.g., swig includes) with the same
        priority."""

        includes = []
        for block_type in block_types:
            try:
                includes += self.includes[block_type]
            except KeyError:
                pass

        return sorted(set(includes))

    def dump_includes(self):
        blocks = []
        # Create a list of blocks in the prescribed include
        # order. Each entry in the list is a multi-line string with
        # multiple includes.
        for types in self.block_order:
            block = "\n".join(self.dump_blocks(types))
            if block:
                blocks.append(block)

        self.reset()
        return "\n\n".join(blocks)

    def __call__(self, lines, filename, language):
        self.reset()

        context = {
            "filename" : filename,
            "language" : language,
            }

        def match_line(line):
            if not line:
                return (None, line)

            for include_type, (ldelim, rdelim), matcher in self.includes_re:
                keyword, include, extra = matcher(context, line)
                if keyword:
                    # if we've got a match, clean up the #include line,
                    # fix up stl headers and store it in the proper category
                    if include_type == 'c' and language == 'C++':
                        stl_inc = cpp_c_headers.get(include, None)
                        if stl_inc:
                            include = stl_inc
                            include_type = 'stl'

                    return (include_type,
                            keyword + ' ' + ldelim + include + rdelim + extra)

            return (None, line)

        processing_includes = False
        for line in lines:
            include_type, line = match_line(line)
            if include_type:
                try:
                    self.includes[include_type].append(line)
                except KeyError:
                    self.includes[include_type] = [ line ]

                processing_includes = True
            elif processing_includes and not line.strip():
                # Skip empty lines while processing includes
                pass
            elif processing_includes:
                # We are now exiting an include block
                processing_includes = False

                # Output pending includes, a new line between, and the
                # current l.
                yield self.dump_includes()
                yield ''
                yield line
            else:
                # We are not in an include block, so just emit the line
                yield line

        # We've reached EOF, so dump any pending includes
        if processing_includes:
            yield self.dump_includes()

# default language types to try to apply our sorting rules to
default_languages = frozenset(('C', 'C++', 'isa', 'python', 'scons', 'swig'))

def options():
    import optparse
    options = optparse.OptionParser()
    add_option = options.add_option
    add_option('-d', '--dir_ignore', metavar="DIR[,DIR]", type='string',
               default=','.join(default_dir_ignore),
               help="ignore directories")
    add_option('-f', '--file_ignore', metavar="FILE[,FILE]", type='string',
               default=','.join(default_file_ignore),
               help="ignore files")
    add_option('-l', '--languages', metavar="LANG[,LANG]", type='string',
               default=','.join(default_languages),
               help="languages")
    add_option('-n', '--dry-run', action='store_true',
               help="don't overwrite files")

    return options

def parse_args(parser):
    opts,args = parser.parse_args()

    opts.dir_ignore = frozenset(opts.dir_ignore.split(','))
    opts.file_ignore = frozenset(opts.file_ignore.split(','))
    opts.languages = frozenset(opts.languages.split(','))

    return opts,args

if __name__ == '__main__':
    parser = options()
    opts, args = parse_args(parser)

    for base in args:
        for filename,language in find_files(base, languages=opts.languages,
                file_ignore=opts.file_ignore, dir_ignore=opts.dir_ignore):
            if opts.dry_run:
                print "%s: %s" % (filename, language)
            else:
                update_file(filename, filename, language, SortIncludes())
<commit_msg>style: Change include sorter to yield one line at a time<commit_after>#!/usr/bin/env python
#
# Copyright (c) 2014-2015 ARM Limited
# All rights reserved
#
# The license below extends only to copyright in the software and shall
# not be construed as granting a license to any other intellectual
# property including but not limited to intellectual property relating
# to a hardware implementation of the functionality of the software
# licensed hereunder.  You may use the software subject to the license
# terms below provided that you ensure that this notice is replicated
# unmodified and in its entirety in all distributions of the software,
# modified or unmodified, in source code or in binary form.
#
# Copyright (c) 2011 The Hewlett-Packard Development Company
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met: redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer;
# 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;
# neither the name of the copyright holders nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# 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.
#
# Authors: Nathan Binkert
#          Andreas Sandberg

import os
import re
import sys

from file_types import *

cpp_c_headers = {
    'assert.h' : 'cassert',
    'ctype.h'  : 'cctype',
    'errno.h'  : 'cerrno',
    'float.h'  : 'cfloat',
    'limits.h' : 'climits',
    'locale.h' : 'clocale',
    'math.h'   : 'cmath',
    'setjmp.h' : 'csetjmp',
    'signal.h' : 'csignal',
    'stdarg.h' : 'cstdarg',
    'stddef.h' : 'cstddef',
    'stdio.h'  : 'cstdio',
    'stdlib.h' : 'cstdlib',
    'string.h' : 'cstring',
    'time.h'   : 'ctime',
    'wchar.h'  : 'cwchar',
    'wctype.h' : 'cwctype',
}

include_re = re.compile(r'([#%])(include|import).*[<"](.*)[">]')
def include_key(line):
    '''Mark directories with a leading space so directories
    are sorted before files'''

    match = include_re.match(line)
    assert match, line
    keyword = match.group(2)
    include = match.group(3)

    # Everything but the file part needs to have a space prepended
    parts = include.split('/')
    if len(parts) == 2 and parts[0] == 'dnet':
        # Don't sort the dnet includes with respect to each other, but
        # make them sorted with respect to non dnet includes.  Python
        # guarantees that sorting is stable, so just clear the
        # basename part of the filename.
        parts[1] = ' '
    parts[0:-1] = [ ' ' + s for s in parts[0:-1] ]
    key = '/'.join(parts)

    return key

def _include_matcher(keyword="#include", delim="<>"):
    """Match an include statement and return a (keyword, file, extra)
    duple, or a touple of None values if there isn't a match."""

    rex = re.compile(r'^(%s)\s*%s(.*)%s(.*)$' % (keyword, delim[0], delim[1]))

    def matcher(context, line):
        m = rex.match(line)
        return m.groups() if m else (None, ) * 3

    return matcher

def _include_matcher_fname(fname, **kwargs):
    """Match an include of a specific file name. Any keyword arguments
    are forwarded to _include_matcher, which is used to match the
    actual include line."""

    rex = re.compile(fname)
    base_matcher = _include_matcher(**kwargs)

    def matcher(context, line):
        (keyword, fname, extra) = base_matcher(context, line)
        if fname and rex.match(fname):
            return (keyword, fname, extra)
        else:
            return (None, ) * 3

    return matcher

def _include_matcher_main():
    """Match a C/C++ source file's primary header (i.e., a file with
    the same base name, but a header extension)."""

    base_matcher = _include_matcher(delim='""')
    rex = re.compile(r"^src/(.*)\.([^.]+)$")
    header_map = {
        "c" : "h",
        "cc" : "hh",
        "cpp" : "hh",
        }
    def matcher(context, line):
        m = rex.match(context["filename"])
        if not m:
            return (None, ) * 3
        base, ext = m.groups()
        (keyword, fname, extra) = base_matcher(context, line)
        try:
            if fname == "%s.%s" % (base, header_map[ext]):
                return (keyword, fname, extra)
        except KeyError:
            pass

        return (None, ) * 3

    return matcher

class SortIncludes(object):
    # different types of includes for different sorting of headers
    # <Python.h>         - Python header needs to be first if it exists
    # <*.h>              - system headers (directories before files)
    # <*>                - STL headers
    # <*.(hh|hxx|hpp|H)> - C++ Headers (directories before files)
    # "*"                - M5 headers (directories before files)
    includes_re = (
        ('main', '""', _include_matcher_main()),
        ('python', '<>', _include_matcher_fname("^Python\.h$")),
        ('c', '<>', _include_matcher_fname("^.*\.h$")),
        ('stl', '<>', _include_matcher_fname("^\w+$")),
        ('cc', '<>', _include_matcher_fname("^.*\.(hh|hxx|hpp|H)$")),
        ('m5header', '""', _include_matcher_fname("^.*\.h{1,2}$", delim='""')),
        ('swig0', '<>', _include_matcher(keyword="%import")),
        ('swig1', '<>', _include_matcher(keyword="%include")),
        ('swig2', '""', _include_matcher(keyword="%import", delim='""')),
        ('swig3', '""', _include_matcher(keyword="%include", delim='""')),
        )

    block_order = (
        ('main', ),
        ('python', ),
        ('c', ),
        ('stl', ),
        ('cc', ),
        ('m5header', ),
        ('swig0', 'swig1', 'swig2', 'swig3', ),
        )

    def __init__(self):
        self.block_priority = {}
        for prio, keys in enumerate(self.block_order):
            for key in keys:
                self.block_priority[key] = prio

    def reset(self):
        # clear all stored headers
        self.includes = {}

    def dump_blocks(self, block_types):
        """Merge includes of from several block types into one large
        block of sorted includes. This is useful when we have multiple
        include block types (e.g., swig includes) with the same
        priority."""

        includes = []
        for block_type in block_types:
            try:
                includes += self.includes[block_type]
            except KeyError:
                pass

        return sorted(set(includes))

    def dump_includes(self):
        includes = []
        for types in self.block_order:
            block = self.dump_blocks(types)
            if includes and block:
                includes.append("")
            includes += block

        self.reset()
        return includes

    def __call__(self, lines, filename, language):
        self.reset()

        context = {
            "filename" : filename,
            "language" : language,
            }

        def match_line(line):
            if not line:
                return (None, line)

            for include_type, (ldelim, rdelim), matcher in self.includes_re:
                keyword, include, extra = matcher(context, line)
                if keyword:
                    # if we've got a match, clean up the #include line,
                    # fix up stl headers and store it in the proper category
                    if include_type == 'c' and language == 'C++':
                        stl_inc = cpp_c_headers.get(include, None)
                        if stl_inc:
                            include = stl_inc
                            include_type = 'stl'

                    return (include_type,
                            keyword + ' ' + ldelim + include + rdelim + extra)

            return (None, line)

        processing_includes = False
        for line in lines:
            include_type, line = match_line(line)
            if include_type:
                try:
                    self.includes[include_type].append(line)
                except KeyError:
                    self.includes[include_type] = [ line ]

                processing_includes = True
            elif processing_includes and not line.strip():
                # Skip empty lines while processing includes
                pass
            elif processing_includes:
                # We are now exiting an include block
                processing_includes = False

                # Output pending includes, a new line between, and the
                # current l.
                for include in self.dump_includes():
                    yield include
                yield ''
                yield line
            else:
                # We are not in an include block, so just emit the line
                yield line

        # We've reached EOF, so dump any pending includes
        if processing_includes:
            for include in self.dump_includes():
                yield include

# default language types to try to apply our sorting rules to
default_languages = frozenset(('C', 'C++', 'isa', 'python', 'scons', 'swig'))

def options():
    import optparse
    options = optparse.OptionParser()
    add_option = options.add_option
    add_option('-d', '--dir_ignore', metavar="DIR[,DIR]", type='string',
               default=','.join(default_dir_ignore),
               help="ignore directories")
    add_option('-f', '--file_ignore', metavar="FILE[,FILE]", type='string',
               default=','.join(default_file_ignore),
               help="ignore files")
    add_option('-l', '--languages', metavar="LANG[,LANG]", type='string',
               default=','.join(default_languages),
               help="languages")
    add_option('-n', '--dry-run', action='store_true',
               help="don't overwrite files")

    return options

def parse_args(parser):
    opts,args = parser.parse_args()

    opts.dir_ignore = frozenset(opts.dir_ignore.split(','))
    opts.file_ignore = frozenset(opts.file_ignore.split(','))
    opts.languages = frozenset(opts.languages.split(','))

    return opts,args

if __name__ == '__main__':
    parser = options()
    opts, args = parse_args(parser)

    for base in args:
        for filename,language in find_files(base, languages=opts.languages,
                file_ignore=opts.file_ignore, dir_ignore=opts.dir_ignore):
            if opts.dry_run:
                print "%s: %s" % (filename, language)
            else:
                update_file(filename, filename, language, SortIncludes())
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  utilities/__init__.py
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * 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.
#  * Neither the name of the  nor the names of its
#    contributors may be used to endorse or promote products derived from
#    this software without specific prior written permission.
#
#  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.
#

import collections
import functools
import os
import re
import time

__version__ = '0.1'

class Cache(object):
	"""
	This class provides a simple to use cache object which can be applied
	as a decorator.
	"""
	def __init__(self, timeout):
		"""
		@type timeout: string, integer
		@param timeout: The amount of time in seconds that a cached
		result will be considered valid for.
		"""
		if isinstance(timeout, (str, unicode)):
			timeout = timedef_to_seconds(timeout)
		self.cache_timeout = timeout
		self.__cache = {}

	def __call__(self, *args):
		if not hasattr(self, '_target_function'):
			self._target_function = args[0]
			return self
		self.cache_clean()
		if not isinstance(args, collections.Hashable):
			return self._target_function(*args)
		result, expiration = self.__cache.get(args, (None, 0))
		if expiration > time.time():
			return result
		result = self._target_function(*args)
		self.__cache[args] = (result, time.time() + self.cache_timeout)
		return result

	def __repr__(self):
		return "<cached function {0}>".format(self._target_function.__name__)

	def cache_clean(self):
		"""
		Remove expired items from the cache.
		"""
		now = time.time()
		keys_for_removal = []
		for key, (value, expiration) in self.__cache.items():
			if expiration < now:
				keys_for_removal.append(key)
		for key in keys_for_removal:
			del self.__cache[key]

	def cache_clear(self):
		"""
		Remove all items from the cache.
		"""
		self.__cache = {}

class FileWalker:
	"""
	This class is used to easily iterate over files in a directory.
	"""
	def __init__(self, filespath, absolute_path = False, skip_files = False, skip_dirs = False, filter_func = None):
		"""
		@type filespath: string
		@param filespath: A path to either a file or a directory.  If a
		file is passed then that will be the only file returned during the
		iteration.  If a directory is passed, all files will be recursively
		returned during the iteration.

		@type absolute_path: boolean
		@param absolute_path: Whether or not the absolute path or a relative
		path should be returned.

		@type skip_files: boolean
		@param skip_files: Whether or not to skip files.

		@type skip_dirs: boolean
		@param skip_dirs: Whether or not to skip directories.

		@type filter_func: function
		@param filter_func: If defined, the filter_func function will be called
		for each file and if the function returns false the file will be
		skipped.
		"""
		if not (os.path.isfile(filespath) or os.path.isdir(filespath)):
			raise Exception(filespath + ' is neither a file or directory')
		if absolute_path:
			self.filespath = os.path.abspath(filespath)
		else:
			self.filespath = os.path.relpath(filespath)
		self.skip_files = skip_files
		self.skip_dirs = skip_dirs
		self.filter_func = filter_func
		if os.path.isdir(self.filespath):
			self.__iter__= self._next_dir
		elif os.path.isfile(self.filespath):
			self.__iter__ = self._next_file

	def _skip(self, cur_file):
		if self.skip_files and os.path.isfile(cur_file):
			return True
		if self.skip_dirs and os.path.isdir(cur_file):
			return True
		if self.filter_func != None:
			if not self.filter_func(cur_file):
				return True
		return False

	def _next_dir(self):
		for root, dirs, files in os.walk(self.filespath):
			for cur_file in files:
				cur_file = os.path.join(root, cur_file)
				if not self._skip(cur_file):
					yield cur_file
			for cur_dir in dirs:
				cur_dir = os.path.join(root, cur_dir)
				if not self._skip(cur_dir):
					yield cur_dir
		raise StopIteration

	def _next_file(self):
		if not self._skip(self.filespath):
			yield self.filespath
		raise StopIteration

class SectionConfigParser(object):
	"""
	Proxy access to a section of a ConfigParser object.
	"""
	__version__ = '0.1'
	def __init__(self, section_name, config_parser):
		"""
		@type section_name: string
		@param section_name: Name of the section to proxy access for.

		@type config_parser: ConfigParser.ConfigParser instance
		@param config_parser: ConfigParser object to proxy access for.
		"""
		self.section_name = section_name
		self.config_parser = config_parser

	def get_raw(self, option, opt_type, default = None):
		get_func = getattr(self.config_parser, 'get' + opt_type)
		if default == None:
			return get_func(self.section_name, option)
		elif self.config_parser.has_option(self.section_name, option):
			return get_func(self.section_name, option)
		else:
			return default

	def get(self, option, default = None):
		return self.get_raw(option, '', default)

	def getint(self, option, default = None):
		return self.get_raw(option, 'int', default)

	def getfloat(self, option, default = None):
		return self.get_raw(option, 'float', default)

	def getboolean(self, option, default = None):
		return self.get_raw(option, 'boolean', default)

	def has_option(self, option):
		return self.config_parser.has_option(self.section_name, option)

	def options(self):
		return self.config_parser.options(self.section_name)

	def items(self):
		return self.config_parser.items(self.section_name)

def parse_case_camel_to_snake(string):
	return re.sub('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))', r'_\1', string).lower()

def parse_case_snake_to_camel(string, upper_first = True):
	string = string.split('_')
	first_part = string[0]
	if upper_first:
		first_part = first_part.title()
	second_part = ''.join(map(lambda word: word.title(), string[1:]))
	return first_part + second_part

def parse_server(server, default_port):
	"""
	Convert a server string to a tuple suitable for passing to connect, for
	example converting 'www.google.com:443' to ('www.google.com', 443).

	@type server: string
	@param server: The server string to convert.

	@type default_port: integer
	@param default_port: The port to use in case one is not specified in
	the server string.
	"""
	server = server.split(':')
	host = server[0]
	if len(server) == 1:
		return (host, default_port)
	else:
		port = server[1]
		if not port:
			port = default_port
		else:
			port = int(port)
		return (host, port)

def parse_timespan(timedef):
	"""
	Convert a string timespan definition to seconds, for example converting
	'1m30s' to 90.

	@type timedef: string
	@param timedef: The timespan definition to convert to seconds.
	"""
	converter_order = ['w', 'd', 'h', 'm', 's']
	converters = {
		'w': 604800,
		'd': 86400,
		'h': 3600,
		'm': 60,
		's': 1
	}
	timedef = timedef.lower()
	seconds = 0
	for spec in converter_order:
		timedef = timedef.split(spec)
		if len(timedef) == 1:
			timedef = timedef[0]
			continue
		elif len(timedef) > 2 or not timedef[0].isdigit():
			raise ValueError('invalid time format')
		adjustment = converters[spec]
		seconds += (int(timedef[0]) * adjustment)
		timedef = timedef[1]
		if not len(timedef):
			break
	if timedef.isdigit():
		seconds += int(timedef)
	return seconds

def unique(seq, key = None):
	"""
	Unique a list or tuple and preserve the order

	@type seq: list, tuple
	@param seq: The list or tuple to preserve unique items from.

	@type key: Function or None
	@param key: If key is provided it will be called during the
	comparison process.
	"""
	if key is None:
		key = lambda x: x
	preserved_type = type(seq)
	seen = {}
	result = []
	for item in seq:
		marker = key(item)
		if marker in seen:
			continue
		seen[marker] = 1
		result.append(item)
	return preserved_type(result)

def which(program):
	"""
	Locate an executable binary's full path by its name.

	@type program: string
	@param program: The executables name.
	"""
	is_exe = lambda fpath: (os.path.isfile(fpath) and os.access(fpath, os.X_OK))
	if is_exe(program):
		return program
	for path in os.environ["PATH"].split(os.pathsep):
		path = path.strip('"')
		exe_file = os.path.join(path, program)
		if is_exe(exe_file):
			return exe_file
	return None

def xfrange(start, stop, step):
	"""
	Iterate through an arithmetic progression.

	@type start: int, long, float
	@param start: Starting number.

	@type stop: int, long, float
	@param stop: Stopping number.

	@type step: int, long, float
	@param step: Stepping size.
	"""
	while start < stop:
		yield start
		start += step
<commit_msg>Improve the timespan parsing function<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  utilities/__init__.py
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * 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.
#  * Neither the name of the  nor the names of its
#    contributors may be used to endorse or promote products derived from
#    this software without specific prior written permission.
#
#  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.
#

import collections
import functools
import os
import re
import time

__version__ = '0.1'

class Cache(object):
	"""
	This class provides a simple to use cache object which can be applied
	as a decorator.
	"""
	def __init__(self, timeout):
		"""
		@type timeout: string, integer
		@param timeout: The amount of time in seconds that a cached
		result will be considered valid for.
		"""
		if isinstance(timeout, (str, unicode)):
			timeout = timedef_to_seconds(timeout)
		self.cache_timeout = timeout
		self.__cache = {}

	def __call__(self, *args):
		if not hasattr(self, '_target_function'):
			self._target_function = args[0]
			return self
		self.cache_clean()
		if not isinstance(args, collections.Hashable):
			return self._target_function(*args)
		result, expiration = self.__cache.get(args, (None, 0))
		if expiration > time.time():
			return result
		result = self._target_function(*args)
		self.__cache[args] = (result, time.time() + self.cache_timeout)
		return result

	def __repr__(self):
		return "<cached function {0}>".format(self._target_function.__name__)

	def cache_clean(self):
		"""
		Remove expired items from the cache.
		"""
		now = time.time()
		keys_for_removal = []
		for key, (value, expiration) in self.__cache.items():
			if expiration < now:
				keys_for_removal.append(key)
		for key in keys_for_removal:
			del self.__cache[key]

	def cache_clear(self):
		"""
		Remove all items from the cache.
		"""
		self.__cache = {}

class FileWalker:
	"""
	This class is used to easily iterate over files in a directory.
	"""
	def __init__(self, filespath, absolute_path = False, skip_files = False, skip_dirs = False, filter_func = None):
		"""
		@type filespath: string
		@param filespath: A path to either a file or a directory.  If a
		file is passed then that will be the only file returned during the
		iteration.  If a directory is passed, all files will be recursively
		returned during the iteration.

		@type absolute_path: boolean
		@param absolute_path: Whether or not the absolute path or a relative
		path should be returned.

		@type skip_files: boolean
		@param skip_files: Whether or not to skip files.

		@type skip_dirs: boolean
		@param skip_dirs: Whether or not to skip directories.

		@type filter_func: function
		@param filter_func: If defined, the filter_func function will be called
		for each file and if the function returns false the file will be
		skipped.
		"""
		if not (os.path.isfile(filespath) or os.path.isdir(filespath)):
			raise Exception(filespath + ' is neither a file or directory')
		if absolute_path:
			self.filespath = os.path.abspath(filespath)
		else:
			self.filespath = os.path.relpath(filespath)
		self.skip_files = skip_files
		self.skip_dirs = skip_dirs
		self.filter_func = filter_func
		if os.path.isdir(self.filespath):
			self.__iter__= self._next_dir
		elif os.path.isfile(self.filespath):
			self.__iter__ = self._next_file

	def _skip(self, cur_file):
		if self.skip_files and os.path.isfile(cur_file):
			return True
		if self.skip_dirs and os.path.isdir(cur_file):
			return True
		if self.filter_func != None:
			if not self.filter_func(cur_file):
				return True
		return False

	def _next_dir(self):
		for root, dirs, files in os.walk(self.filespath):
			for cur_file in files:
				cur_file = os.path.join(root, cur_file)
				if not self._skip(cur_file):
					yield cur_file
			for cur_dir in dirs:
				cur_dir = os.path.join(root, cur_dir)
				if not self._skip(cur_dir):
					yield cur_dir
		raise StopIteration

	def _next_file(self):
		if not self._skip(self.filespath):
			yield self.filespath
		raise StopIteration

class SectionConfigParser(object):
	"""
	Proxy access to a section of a ConfigParser object.
	"""
	__version__ = '0.1'
	def __init__(self, section_name, config_parser):
		"""
		@type section_name: string
		@param section_name: Name of the section to proxy access for.

		@type config_parser: ConfigParser.ConfigParser instance
		@param config_parser: ConfigParser object to proxy access for.
		"""
		self.section_name = section_name
		self.config_parser = config_parser

	def get_raw(self, option, opt_type, default = None):
		get_func = getattr(self.config_parser, 'get' + opt_type)
		if default == None:
			return get_func(self.section_name, option)
		elif self.config_parser.has_option(self.section_name, option):
			return get_func(self.section_name, option)
		else:
			return default

	def get(self, option, default = None):
		return self.get_raw(option, '', default)

	def getint(self, option, default = None):
		return self.get_raw(option, 'int', default)

	def getfloat(self, option, default = None):
		return self.get_raw(option, 'float', default)

	def getboolean(self, option, default = None):
		return self.get_raw(option, 'boolean', default)

	def has_option(self, option):
		return self.config_parser.has_option(self.section_name, option)

	def options(self):
		return self.config_parser.options(self.section_name)

	def items(self):
		return self.config_parser.items(self.section_name)

def parse_case_camel_to_snake(string):
	return re.sub('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))', r'_\1', string).lower()

def parse_case_snake_to_camel(string, upper_first = True):
	string = string.split('_')
	first_part = string[0]
	if upper_first:
		first_part = first_part.title()
	second_part = ''.join(map(lambda word: word.title(), string[1:]))
	return first_part + second_part

def parse_server(server, default_port):
	"""
	Convert a server string to a tuple suitable for passing to connect, for
	example converting 'www.google.com:443' to ('www.google.com', 443).

	@type server: string
	@param server: The server string to convert.

	@type default_port: integer
	@param default_port: The port to use in case one is not specified in
	the server string.
	"""
	server = server.split(':')
	host = server[0]
	if len(server) == 1:
		return (host, default_port)
	else:
		port = server[1]
		if not port:
			port = default_port
		else:
			port = int(port)
		return (host, port)

def parse_timespan(timedef):
	"""
	Convert a string timespan definition to seconds, for example converting
	'1m30s' to 90.

	@type timedef: string
	@param timedef: The timespan definition to convert to seconds.
	"""
	converter_order = ['w', 'd', 'h', 'm', 's']
	converters = {
		'w': 604800,
		'd': 86400,
		'h': 3600,
		'm': 60,
		's': 1
	}
	timedef = timedef.lower()
	if timedef.isdigit():
		return int(timedef)
	elif len(timedef) == 0:
		return 0
	seconds = -1
	for spec in converter_order:
		timedef = timedef.split(spec)
		if len(timedef) == 1:
			timedef = timedef[0]
			continue
		elif len(timedef) > 2 or not timedef[0].isdigit():
			seconds = -1
			break
		adjustment = converters[spec]
		seconds = max(seconds, 0)
		seconds += (int(timedef[0]) * adjustment)
		timedef = timedef[1]
		if not len(timedef):
			break
	if seconds < 0:
		raise ValueError('invalid time format')
	return seconds

def unique(seq, key = None):
	"""
	Unique a list or tuple and preserve the order

	@type seq: list, tuple
	@param seq: The list or tuple to preserve unique items from.

	@type key: Function or None
	@param key: If key is provided it will be called during the
	comparison process.
	"""
	if key is None:
		key = lambda x: x
	preserved_type = type(seq)
	seen = {}
	result = []
	for item in seq:
		marker = key(item)
		if marker in seen:
			continue
		seen[marker] = 1
		result.append(item)
	return preserved_type(result)

def which(program):
	"""
	Locate an executable binary's full path by its name.

	@type program: string
	@param program: The executables name.
	"""
	is_exe = lambda fpath: (os.path.isfile(fpath) and os.access(fpath, os.X_OK))
	if is_exe(program):
		return program
	for path in os.environ["PATH"].split(os.pathsep):
		path = path.strip('"')
		exe_file = os.path.join(path, program)
		if is_exe(exe_file):
			return exe_file
	return None

def xfrange(start, stop, step):
	"""
	Iterate through an arithmetic progression.

	@type start: int, long, float
	@param start: Starting number.

	@type stop: int, long, float
	@param stop: Stopping number.

	@type step: int, long, float
	@param step: Stepping size.
	"""
	while start < stop:
		yield start
		start += step
<|endoftext|>


<commit_before>TYPEMAP = {
    "geo": "Location",
    "cip": "Education",
    "naics": "Industry",
    "soc": "Occupation",
    "story": "Story",
    "map": "Geomap"
}

HOMEFEED = [
    {
        "link": "/profile/geo/new-york-ny/#income_gender",
        "subtitle": "What is the gender wage gap for the 5 most common occupations?",
        "section": "economy",
        "viz": "bar_double"
    },
    {
        "link": "/story/11-19-2015_cost-of-living/",
        "featured": True
    },
    {
        "link": "/profile/soc/291060/#employment",
        "subtitle": "Where in the U.S. are Physicians & Surgeons paid the most?",
        "section": "employment",
        "viz": "geo_map"
    },
    {
        "link": "/profile/cip/4506/#skills",
        "subtitle": "Which skills are most relevant for Economics students?",
        "section": "skills",
        "viz": "radar"
    },
    {
        "link": "/profile/soc/172051/#growth",
        "subtitle": "What is the estimated job growth of Civil Engineers over the next 10 years, & how does it compare to the estimated national average?",
        "section": "growth",
        "viz": "line",
        "featured": True
    },
    {
        "link": "/map/?level=county&key=age,age_moe,age_rank",
        "title": "Map a Generation",
        "subtitle": "Which regions of the U.S. have the youngest average population age?"
    },
    {
        "link": "/profile/geo/philadelphia-pa/#housing",
        "subtitle": "What is the spread of median household incomes compared to that of the U.S.?",
        "section": "housing",
        "viz": "bar_dist"
    },
    {
        "link": "/profile/cip/110701/#institutions",
        "subtitle": "Which private or public institutions award the most degrees in Computer Science per year?",
        "section": "institutions",
        "viz": "tree_map"
    },
    {
        "link": "/story/11-16-2015_minority-education/",
        "featured": False
    },
    {
        "link": "/profile/geo/los-angeles-ca/#health",
        "subtitle": "What is the prevalence of obesity & diabetes in California by county?",
        "section": "health",
        "viz": "scatter",
        "featured": True
    },
    {
        "link": "/profile/geo/st.-louis-mo/#education",
        "subtitle": "What are the most common & most specialized university majors in St. Louis?",
        "section": "education",
        "viz": "tree_map"
    },
    {
        "link": "/profile/naics/531/#workforce",
        "subtitle": "What are the 5 most common, specialized, & highest paid jobs within the industry?",
        "section": "workforce",
        "viz": "bar_triptych"
    }
]
<commit_msg>one more block for home feed<commit_after>TYPEMAP = {
    "geo": "Location",
    "cip": "Education",
    "naics": "Industry",
    "soc": "Occupation",
    "story": "Story",
    "map": "Geomap"
}

HOMEFEED = [
    {
        "link": "/profile/geo/new-york-ny/#income_gender",
        "subtitle": "What is the gender wage gap for the 5 most common occupations?",
        "section": "economy",
        "viz": "bar_double"
    },
    {
        "link": "/story/11-19-2015_cost-of-living/",
        "featured": True
    },
    {
        "link": "/profile/soc/291060/#employment",
        "subtitle": "Where in the U.S. are Physicians & Surgeons paid the most?",
        "section": "employment",
        "viz": "geo_map"
    },
    {
        "link": "/profile/cip/4506/#skills",
        "subtitle": "Which skills are most relevant for Economics students?",
        "section": "skills",
        "viz": "radar"
    },
    {
        "link": "/profile/soc/172051/#growth",
        "subtitle": "What is the estimated job growth of Civil Engineers over the next 10 years, & how does it compare to the estimated national average?",
        "section": "growth",
        "viz": "line",
        "featured": True
    },
    {
        "link": "/map/?level=county&key=age,age_moe,age_rank",
        "title": "Map a Generation",
        "subtitle": "Which regions of the U.S. have the youngest average population age?"
    },
    {
        "link": "/profile/geo/philadelphia-pa/#housing",
        "subtitle": "What is the spread of median household incomes compared to that of the U.S.?",
        "section": "housing",
        "viz": "bar_dist"
    },
    {
        "link": "/profile/cip/110701/#institutions",
        "subtitle": "Which private or public institutions award the most degrees in Computer Science per year?",
        "section": "institutions",
        "viz": "tree_map"
    },
    {
        "link": "/story/11-16-2015_minority-education/",
        "featured": False
    },
    {
        "link": "/profile/geo/los-angeles-ca/#health",
        "subtitle": "What is the prevalence of obesity & diabetes in California by county?",
        "section": "health",
        "viz": "scatter",
        "featured": True
    },
    {
        "link": "/profile/geo/st.-louis-mo/#education",
        "subtitle": "What are the most common & most specialized university majors in St. Louis?",
        "section": "education",
        "viz": "tree_map"
    },
    {
        "link": "/profile/naics/531/#workforce",
        "subtitle": "What are the 5 most common, specialized, & highest paid jobs within the industry?",
        "section": "workforce",
        "viz": "bar_triptych"
    },
    {
        "link": "/story/11-11-2015_gender-bias-workplace/",
        "featured": False
    }
]
<|endoftext|>