<commit_before>"""

Risk Report
===========

    +-----------------+----------------------------------------------------+
    | key             | value                                              |
    +=================+====================================================+
    | trading_days    | The number of trading days between self.start_date |
    |                 | and self.end_date                                  |
    +-----------------+----------------------------------------------------+
    | benchmark_volat\| The volatility of the benchmark between            |
    | ility           | self.start_date and self.end_date.                 |
    +-----------------+----------------------------------------------------+
    | algo_volatility | The volatility of the algo between self.start_date |
    |                 | and self.end_date.                                 |
    +-----------------+----------------------------------------------------+
    | treasury_period\| The return of treasuries over the period. Treasury |
    | _return         | maturity is chosen to match the duration of the    |
    |                 | test period.                                       |
    +-----------------+----------------------------------------------------+
    | sharpe          | The sharpe ratio based on the _algorithm_ (rather  |
    |                 | than the static portfolio) returns.                |
    +-----------------+----------------------------------------------------+
    | beta            | The _algorithm_ beta to the benchmark.             |
    +-----------------+----------------------------------------------------+
    | alpha           | The _algorithm_ alpha to the benchmark.            |
    +-----------------+----------------------------------------------------+
    | excess_return   | The excess return of the algorithm over the        |
    |                 | benchmark.                                         |
    +-----------------+----------------------------------------------------+
    | max_drawdown    | The largest relative peak to relative trough move  |
    |                 | for the portfolio returns between self.start_date  |
    |                 | and self.end_date.                                 |
    +-----------------+----------------------------------------------------+

"""

import logging
import datetime
import math
import numpy as np
import numpy.linalg as la
from zipline.utils.date_utils import epoch_now

LOGGER = logging.getLogger('ZiplineLogger')

def advance_by_months(dt, jump_in_months):
    month = dt.month + jump_in_months
    years = month / 12
    month = month % 12

    # no remainder means that we are landing in december.
    # modulo is, in a way, a zero indexed circular array.
    # this is a way of converting to 1 indexed months.
    # (in our modulo index, december is zeroth)
    if(month == 0):
        month = 12
        years = years - 1

    return dt.replace(year = dt.year + years, month = month)

class DailyReturn():

    def __init__(self, date, returns):

        assert isinstance(date, datetime.datetime)
        self.date = date.replace(hour=0, minute=0, second=0)
        self.returns = returns

    def to_dict(self):
        return {
            'dt'      : self.date,
            'returns' : self.returns
        }

    def __repr__(self):
        return str(self.date) + " - " + str(self.returns)

class RiskMetrics():
    def __init__(self, start_date, end_date, returns, trading_environment):

        self.treasury_curves = trading_environment.treasury_curves
        self.start_date = start_date
        self.end_date = end_date
        self.trading_environment = trading_environment
        self.algorithm_period_returns, self.algorithm_returns = \
            self.calculate_period_returns(returns)

        benchmark_returns = [
                    x for x in self.trading_environment.benchmark_returns
                    if x.date >= returns[0].date and x.date <= returns[-1].date
        ]

        self.benchmark_period_returns, self.benchmark_returns = \
            self.calculate_period_returns(benchmark_returns)

        if(len(self.benchmark_returns) != len(self.algorithm_returns)):
            message = "Mismatch between benchmark_returns ({bm_count}) and \
            algorithm_returns ({algo_count}) in range {start} : {end}"
            message = message.format(
                bm_count=len(self.benchmark_returns),
                algo_count=len(self.algorithm_returns),
                start=start_date,
                end=end_date
            )
            raise Exception(message)

        self.trading_days = len(self.benchmark_returns)
        self.benchmark_volatility = self.calculate_volatility(self.benchmark_returns)
        self.algorithm_volatility = self.calculate_volatility(self.algorithm_returns)
        self.treasury_period_return = self.choose_treasury()
        self.sharpe = self.calculate_sharpe()
        self.beta, self.algorithm_covariance, self.benchmark_variance, \
        self.condition_number, self.eigen_values = self.calculate_beta()
        self.alpha = self.calculate_alpha()
        self.excess_return = self.algorithm_period_returns - self.treasury_period_return
        self.max_drawdown = self.calculate_max_drawdown()

    def to_dict(self):
        """
        Creates a dictionary representing the state of the risk report.
        Returns a dict object of the form:
        """
        period_label = self.end_date.strftime("%Y-%m")
        return {
            'trading_days'          : self.trading_days,
            'benchmark_volatility'  : self.benchmark_volatility,
            'algo_volatility'       : self.algorithm_volatility,
            'treasury_period_return': self.treasury_period_return,
            'algorithm_period_return' : self.algorithm_period_returns,
            'benchmark_period_return' : self.benchmark_period_returns,
            'sharpe'                : self.sharpe,
            'beta'                  : self.beta,
            'alpha'                 : self.alpha,
            'excess_return'         : self.excess_return,
            'max_drawdown'          : self.max_drawdown,
            'period_label'          : period_label
        }

    def __repr__(self):
        statements = []
        metrics = [
            "algorithm_period_returns" ,
            "benchmark_period_returns" ,
            "excess_return"            ,
            "trading_days"             ,
            "benchmark_volatility"     ,
            "algorithm_volatility"     ,
            "sharpe"                   ,
            "algorithm_covariance"     ,
            "benchmark_variance"       ,
            "beta"                     ,
            "alpha"                    ,
            "max_drawdown"             ,
            "algorithm_returns"        ,
            "benchmark_returns"        ,
            "condition_number"         ,
            "eigen_values"
        ]

        for metric in metrics:
            value = getattr(self, metric)
            statements.append("{m}:{v}".format(m=metric, v=value))

        return '\n'.join(statements)

    def calculate_period_returns(self, daily_returns):

        #TODO: replace this with pandas.
        returns = [
            x.returns for x in daily_returns
            if x.date >= self.start_date and
               x.date <= self.end_date and
               self.trading_environment.is_trading_day(x.date)
        ]

        period_returns = 1.0

        for r in returns:
            period_returns = period_returns * (1.0 + r)

        period_returns = period_returns - 1.0
        return period_returns, returns

    def calculate_volatility(self, daily_returns):
        return np.std(daily_returns, ddof=1) * math.sqrt(self.trading_days)

    def calculate_sharpe(self):
        """
        http://en.wikipedia.org/wiki/Sharpe_ratio
        """
        if self.algorithm_volatility == 0:
            return 0.0

        return ( (self.algorithm_period_returns - self.treasury_period_return) /
            self.algorithm_volatility )

    def calculate_beta(self):
        """

        .. math::
            \beta_a = \frac {\mathrm{Cov}(r_a,r_p)}{\mathrm{Var}(r_p)}

        http://en.wikipedia.org/wiki/Beta_(finance)
        """

        #it doesn't make much sense to calculate beta for less than two days,
        #so return none.
        if len(self.algorithm_returns) < 2:
            return 0.0, 0.0, 0.0, 0.0, []

        returns_matrix = np.vstack([self.algorithm_returns, self.benchmark_returns])
        C = np.cov(returns_matrix)
        eigen_values = la.eigvals(C)
        condition_number = max(eigen_values) / min(eigen_values)
        algorithm_covariance = C[0][1]
        benchmark_variance = C[1][1]
        beta = C[0][1] / C[1][1]

        return (
            beta,
            algorithm_covariance,
            benchmark_variance,
            condition_number,
            eigen_values
        )

    def calculate_alpha(self):
        """
        http://en.wikipedia.org/wiki/Alpha_(investment)
        """
        return self.algorithm_period_returns - (self.treasury_period_return + self.beta * (self.benchmark_period_returns - self.treasury_period_return))

    def calculate_max_drawdown(self):
        compounded_returns = []
        cur_return = 0.0
        for r in self.algorithm_returns:
            try:
                cur_return = math.log(1.0 + r) + cur_return
            #this is a guard for a single day returning -100%
            except ValueError:
                LOGGER.warn("{cur} return, zeroing the returns".format(cur=cur_return))
                cur_return = 0.0
            compounded_returns.append(cur_return)

        cur_max = None
        max_drawdown = None
        for cur in compounded_returns:
            if cur_max == None or cur > cur_max:
                cur_max = cur

            drawdown = (cur - cur_max)
            if max_drawdown == None or drawdown < max_drawdown:
                max_drawdown = drawdown

        if max_drawdown == None:
            return 0.0

        return 1.0 - math.exp(max_drawdown)

    def choose_treasury(self):
        td = self.end_date - self.start_date
        if td.days <= 31:
            self.treasury_duration = '1month'
        elif td.days <= 93:
            self.treasury_duration = '3month'
        elif td.days <= 186:
            self.treasury_duration = '6month'
        elif td.days <= 366:
            self.treasury_duration = '1year'
        elif td.days <= 365 * 2 + 1:
            self.treasury_duration = '2year'
        elif td.days <= 365 * 3 + 1:
            self.treasury_duration = '3year'
        elif td.days <= 365 * 5 + 2:
            self.treasury_duration = '5year'
        elif td.days <= 365 * 7 + 2:
            self.treasury_duration = '7year'
        elif td.days <= 365 * 10 + 2:
            self.treasury_duration = '10year'
        else:
            self.treasury_duration = '30year'

        one_day = datetime.timedelta(days=1)

        curve = None
        # in case end date is not a trading day, search for the next market
        # day for an interest rate
        for i in xrange(7):
            if(self.treasury_curves.has_key(self.end_date + i * one_day)):
                curve = self.treasury_curves[self.end_date + i * one_day]
                break

        if curve:
            self.treasury_curve = curve
            rate = self.treasury_curve[self.treasury_duration]
            #1month note data begins in 8/2001, so we can use 3month instead.
            if rate == None and self.treasury_duration == '1month':
                rate = self.treasury_curve['3month']
            if rate != None:
                return rate * (td.days + 1) / 365

            message = "no rate for end date = {dt} and term = {term}. Check \
            that date doesn't exceed treasury history range."
            message = message.format(
                    dt=self.end_date,
                    term=self.treasury_duration
            )
            raise Exception(message)

class RiskReport():

    def __init__(
        self,
        algorithm_returns,
        trading_environment,
        exceeded_max_loss=False):
        """
        algorithm_returns needs to be a list of daily_return objects
        sorted in date ascending order
        """

        self.algorithm_returns = algorithm_returns
        self.trading_environment = trading_environment
        self.exceeded_max_loss = exceeded_max_loss
        self.created = epoch_now()

        if len(self.algorithm_returns) == 0:
            start_date = self.trading_environment.period_start
            end_date = self.trading_environment.period_end
        else:
            start_date = self.algorithm_returns[0].date
            end_date = self.algorithm_returns[-1].date

        self.month_periods = self.periodsInRange(1, start_date, end_date)
        self.three_month_periods = self.periodsInRange(3, start_date, end_date)
        self.six_month_periods = self.periodsInRange(6, start_date, end_date)
        self.year_periods = self.periodsInRange(12, start_date, end_date)

    def to_dict(self):
        """
        RiskMetrics are calculated for rolling windows in four lengths::
            - 1_month
            - 3_month
            - 6_month
            - 12_month

        The return value of this funciton is a dictionary keyed by the above
        list of durations. The value of each entry is a list of RiskMetric
        dicts of the same duration as denoted by the top_level key.

        See :py:meth:`RiskMetrics.to_dict` for the detailed list of fields
        provided for each period.
        """
        return {
            'one_month'         : [x.to_dict() for x in self.month_periods],
            'three_month'       : [x.to_dict() for x in self.three_month_periods],
            'six_month'         : [x.to_dict() for x in self.six_month_periods],
            'twelve_month'      : [x.to_dict() for x in self.year_periods],
            'exceeded_max_loss' : self.exceeded_max_loss,
            'created'           : self.created
        }

    def periodsInRange(self, months_per, start, end):
        one_day = datetime.timedelta(days = 1)
        ends = []
        cur_start = start.replace(day=1)

        # in edge cases (all sids filtered out, start/end are adjacent)
        # a test will not generate any returns data
        if len(self.algorithm_returns) == 0:
            return ends

        #ensure that we have an end at the end of a calendar month, in case
        #the return series ends mid-month...
        the_end = advance_by_months(end.replace(day=1),1) - one_day
        while True:
            cur_end = advance_by_months(cur_start, months_per) - one_day
            if(cur_end > the_end):
                break
            cur_period_metrics = RiskMetrics(
                start_date=cur_start,
                end_date=cur_end,
                returns=self.algorithm_returns,
                trading_environment=self.trading_environment
            )

            ends.append(cur_period_metrics)
            cur_start = advance_by_months(cur_start, 1)

        return ends

    def find_metric_by_end(self, end_date, duration, metric):
        col = getattr(self, duration + "_periods")
        col = [getattr(x, metric) for x in col if x.end_date == end_date]
        if len(col) == 1:
            return col[0]
        return None
<commit_msg>marked a todo for a calculation bug<commit_after>"""

Risk Report
===========

    +-----------------+----------------------------------------------------+
    | key             | value                                              |
    +=================+====================================================+
    | trading_days    | The number of trading days between self.start_date |
    |                 | and self.end_date                                  |
    +-----------------+----------------------------------------------------+
    | benchmark_volat\| The volatility of the benchmark between            |
    | ility           | self.start_date and self.end_date.                 |
    +-----------------+----------------------------------------------------+
    | algo_volatility | The volatility of the algo between self.start_date |
    |                 | and self.end_date.                                 |
    +-----------------+----------------------------------------------------+
    | treasury_period\| The return of treasuries over the period. Treasury |
    | _return         | maturity is chosen to match the duration of the    |
    |                 | test period.                                       |
    +-----------------+----------------------------------------------------+
    | sharpe          | The sharpe ratio based on the _algorithm_ (rather  |
    |                 | than the static portfolio) returns.                |
    +-----------------+----------------------------------------------------+
    | beta            | The _algorithm_ beta to the benchmark.             |
    +-----------------+----------------------------------------------------+
    | alpha           | The _algorithm_ alpha to the benchmark.            |
    +-----------------+----------------------------------------------------+
    | excess_return   | The excess return of the algorithm over the        |
    |                 | benchmark.                                         |
    +-----------------+----------------------------------------------------+
    | max_drawdown    | The largest relative peak to relative trough move  |
    |                 | for the portfolio returns between self.start_date  |
    |                 | and self.end_date.                                 |
    +-----------------+----------------------------------------------------+

"""

import logging
import datetime
import math
import numpy as np
import numpy.linalg as la
from zipline.utils.date_utils import epoch_now

LOGGER = logging.getLogger('ZiplineLogger')

def advance_by_months(dt, jump_in_months):
    month = dt.month + jump_in_months
    years = month / 12
    month = month % 12

    # no remainder means that we are landing in december.
    # modulo is, in a way, a zero indexed circular array.
    # this is a way of converting to 1 indexed months.
    # (in our modulo index, december is zeroth)
    if(month == 0):
        month = 12
        years = years - 1

    return dt.replace(year = dt.year + years, month = month)

class DailyReturn():

    def __init__(self, date, returns):

        assert isinstance(date, datetime.datetime)
        self.date = date.replace(hour=0, minute=0, second=0)
        self.returns = returns

    def to_dict(self):
        return {
            'dt'      : self.date,
            'returns' : self.returns
        }

    def __repr__(self):
        return str(self.date) + " - " + str(self.returns)

class RiskMetrics():
    def __init__(self, start_date, end_date, returns, trading_environment):

        self.treasury_curves = trading_environment.treasury_curves
        self.start_date = start_date
        self.end_date = end_date
        self.trading_environment = trading_environment
        self.algorithm_period_returns, self.algorithm_returns = \
            self.calculate_period_returns(returns)

        benchmark_returns = [
                    x for x in self.trading_environment.benchmark_returns
                    if x.date >= returns[0].date and x.date <= returns[-1].date
        ]

        self.benchmark_period_returns, self.benchmark_returns = \
            self.calculate_period_returns(benchmark_returns)

        if(len(self.benchmark_returns) != len(self.algorithm_returns)):
            message = "Mismatch between benchmark_returns ({bm_count}) and \
            algorithm_returns ({algo_count}) in range {start} : {end}"
            message = message.format(
                bm_count=len(self.benchmark_returns),
                algo_count=len(self.algorithm_returns),
                start=start_date,
                end=end_date
            )
            raise Exception(message)

        self.trading_days = len(self.benchmark_returns)
        self.benchmark_volatility = self.calculate_volatility(self.benchmark_returns)
        self.algorithm_volatility = self.calculate_volatility(self.algorithm_returns)
        self.treasury_period_return = self.choose_treasury()
        self.sharpe = self.calculate_sharpe()
        self.beta, self.algorithm_covariance, self.benchmark_variance, \
        self.condition_number, self.eigen_values = self.calculate_beta()
        self.alpha = self.calculate_alpha()
        self.excess_return = self.algorithm_period_returns - self.treasury_period_return
        self.max_drawdown = self.calculate_max_drawdown()

    def to_dict(self):
        """
        Creates a dictionary representing the state of the risk report.
        Returns a dict object of the form:
        """
        period_label = self.end_date.strftime("%Y-%m")
        return {
            'trading_days'          : self.trading_days,
            'benchmark_volatility'  : self.benchmark_volatility,
            'algo_volatility'       : self.algorithm_volatility,
            'treasury_period_return': self.treasury_period_return,
            'algorithm_period_return' : self.algorithm_period_returns,
            'benchmark_period_return' : self.benchmark_period_returns,
            'sharpe'                : self.sharpe,
            'beta'                  : self.beta,
            'alpha'                 : self.alpha,
            'excess_return'         : self.excess_return,
            'max_drawdown'          : self.max_drawdown,
            'period_label'          : period_label
        }

    def __repr__(self):
        statements = []
        metrics = [
            "algorithm_period_returns" ,
            "benchmark_period_returns" ,
            "excess_return"            ,
            "trading_days"             ,
            "benchmark_volatility"     ,
            "algorithm_volatility"     ,
            "sharpe"                   ,
            "algorithm_covariance"     ,
            "benchmark_variance"       ,
            "beta"                     ,
            "alpha"                    ,
            "max_drawdown"             ,
            "algorithm_returns"        ,
            "benchmark_returns"        ,
            "condition_number"         ,
            "eigen_values"
        ]

        for metric in metrics:
            value = getattr(self, metric)
            statements.append("{m}:{v}".format(m=metric, v=value))

        return '\n'.join(statements)

    def calculate_period_returns(self, daily_returns):

        #TODO: replace this with pandas.
        returns = [
            x.returns for x in daily_returns
            if x.date >= self.start_date and
               x.date <= self.end_date and
               self.trading_environment.is_trading_day(x.date)
        ]

        period_returns = 1.0

        for r in returns:
            period_returns = period_returns * (1.0 + r)

        period_returns = period_returns - 1.0
        return period_returns, returns

    def calculate_volatility(self, daily_returns):
        # TODO: we should be using an annualized number for the
        # square root, not the days in the period.
        return np.std(daily_returns, ddof=1) * math.sqrt(self.trading_days)

    def calculate_sharpe(self):
        """
        http://en.wikipedia.org/wiki/Sharpe_ratio
        """
        if self.algorithm_volatility == 0:
            return 0.0

        return ( (self.algorithm_period_returns - self.treasury_period_return) /
            self.algorithm_volatility )

    def calculate_beta(self):
        """

        .. math::
            \beta_a = \frac {\mathrm{Cov}(r_a,r_p)}{\mathrm{Var}(r_p)}

        http://en.wikipedia.org/wiki/Beta_(finance)
        """

        #it doesn't make much sense to calculate beta for less than two days,
        #so return none.
        if len(self.algorithm_returns) < 2:
            return 0.0, 0.0, 0.0, 0.0, []

        returns_matrix = np.vstack([self.algorithm_returns, self.benchmark_returns])
        C = np.cov(returns_matrix)
        eigen_values = la.eigvals(C)
        condition_number = max(eigen_values) / min(eigen_values)
        algorithm_covariance = C[0][1]
        benchmark_variance = C[1][1]
        beta = C[0][1] / C[1][1]

        return (
            beta,
            algorithm_covariance,
            benchmark_variance,
            condition_number,
            eigen_values
        )

    def calculate_alpha(self):
        """
        http://en.wikipedia.org/wiki/Alpha_(investment)
        """
        return self.algorithm_period_returns - (self.treasury_period_return + self.beta * (self.benchmark_period_returns - self.treasury_period_return))

    def calculate_max_drawdown(self):
        compounded_returns = []
        cur_return = 0.0
        for r in self.algorithm_returns:
            try:
                cur_return = math.log(1.0 + r) + cur_return
            #this is a guard for a single day returning -100%
            except ValueError:
                LOGGER.warn("{cur} return, zeroing the returns".format(cur=cur_return))
                cur_return = 0.0
            compounded_returns.append(cur_return)

        cur_max = None
        max_drawdown = None
        for cur in compounded_returns:
            if cur_max == None or cur > cur_max:
                cur_max = cur

            drawdown = (cur - cur_max)
            if max_drawdown == None or drawdown < max_drawdown:
                max_drawdown = drawdown

        if max_drawdown == None:
            return 0.0

        return 1.0 - math.exp(max_drawdown)

    def choose_treasury(self):
        td = self.end_date - self.start_date
        if td.days <= 31:
            self.treasury_duration = '1month'
        elif td.days <= 93:
            self.treasury_duration = '3month'
        elif td.days <= 186:
            self.treasury_duration = '6month'
        elif td.days <= 366:
            self.treasury_duration = '1year'
        elif td.days <= 365 * 2 + 1:
            self.treasury_duration = '2year'
        elif td.days <= 365 * 3 + 1:
            self.treasury_duration = '3year'
        elif td.days <= 365 * 5 + 2:
            self.treasury_duration = '5year'
        elif td.days <= 365 * 7 + 2:
            self.treasury_duration = '7year'
        elif td.days <= 365 * 10 + 2:
            self.treasury_duration = '10year'
        else:
            self.treasury_duration = '30year'

        one_day = datetime.timedelta(days=1)

        curve = None
        # in case end date is not a trading day, search for the next market
        # day for an interest rate
        for i in xrange(7):
            if(self.treasury_curves.has_key(self.end_date + i * one_day)):
                curve = self.treasury_curves[self.end_date + i * one_day]
                break

        if curve:
            self.treasury_curve = curve
            rate = self.treasury_curve[self.treasury_duration]
            #1month note data begins in 8/2001, so we can use 3month instead.
            if rate == None and self.treasury_duration == '1month':
                rate = self.treasury_curve['3month']
            if rate != None:
                return rate * (td.days + 1) / 365

            message = "no rate for end date = {dt} and term = {term}. Check \
            that date doesn't exceed treasury history range."
            message = message.format(
                    dt=self.end_date,
                    term=self.treasury_duration
            )
            raise Exception(message)

class RiskReport():

    def __init__(
        self,
        algorithm_returns,
        trading_environment,
        exceeded_max_loss=False):
        """
        algorithm_returns needs to be a list of daily_return objects
        sorted in date ascending order
        """

        self.algorithm_returns = algorithm_returns
        self.trading_environment = trading_environment
        self.exceeded_max_loss = exceeded_max_loss
        self.created = epoch_now()

        if len(self.algorithm_returns) == 0:
            start_date = self.trading_environment.period_start
            end_date = self.trading_environment.period_end
        else:
            start_date = self.algorithm_returns[0].date
            end_date = self.algorithm_returns[-1].date

        self.month_periods = self.periodsInRange(1, start_date, end_date)
        self.three_month_periods = self.periodsInRange(3, start_date, end_date)
        self.six_month_periods = self.periodsInRange(6, start_date, end_date)
        self.year_periods = self.periodsInRange(12, start_date, end_date)

    def to_dict(self):
        """
        RiskMetrics are calculated for rolling windows in four lengths::
            - 1_month
            - 3_month
            - 6_month
            - 12_month

        The return value of this funciton is a dictionary keyed by the above
        list of durations. The value of each entry is a list of RiskMetric
        dicts of the same duration as denoted by the top_level key.

        See :py:meth:`RiskMetrics.to_dict` for the detailed list of fields
        provided for each period.
        """
        return {
            'one_month'         : [x.to_dict() for x in self.month_periods],
            'three_month'       : [x.to_dict() for x in self.three_month_periods],
            'six_month'         : [x.to_dict() for x in self.six_month_periods],
            'twelve_month'      : [x.to_dict() for x in self.year_periods],
            'exceeded_max_loss' : self.exceeded_max_loss,
            'created'           : self.created
        }

    def periodsInRange(self, months_per, start, end):
        one_day = datetime.timedelta(days = 1)
        ends = []
        cur_start = start.replace(day=1)

        # in edge cases (all sids filtered out, start/end are adjacent)
        # a test will not generate any returns data
        if len(self.algorithm_returns) == 0:
            return ends

        #ensure that we have an end at the end of a calendar month, in case
        #the return series ends mid-month...
        the_end = advance_by_months(end.replace(day=1),1) - one_day
        while True:
            cur_end = advance_by_months(cur_start, months_per) - one_day
            if(cur_end > the_end):
                break
            cur_period_metrics = RiskMetrics(
                start_date=cur_start,
                end_date=cur_end,
                returns=self.algorithm_returns,
                trading_environment=self.trading_environment
            )

            ends.append(cur_period_metrics)
            cur_start = advance_by_months(cur_start, 1)

        return ends

    def find_metric_by_end(self, end_date, duration, metric):
        col = getattr(self, duration + "_periods")
        col = [getattr(x, metric) for x in col if x.end_date == end_date]
        if len(col) == 1:
            return col[0]
        return None
<|endoftext|>


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

from flask import Blueprint

web_bp = Blueprint('web', __name__, template_folder='templates', static_folder='static')

import zoe_web.web.start
import zoe_web.web.status
import zoe_web.web.applications

from zoe_lib.version import ZOE_API_VERSION, ZOE_VERSION

@web_bp.context_processor
def inject_version():
    return {
        'zoe_lib_version': ZOE_VERSION,
        'zoe_api_version': ZOE_API_VERSION,
    }
<commit_msg>Fix version in web pages<commit_after># Copyright (c) 2015, Daniele Venzano
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from flask import Blueprint

web_bp = Blueprint('web', __name__, template_folder='templates', static_folder='static')

import zoe_web.web.start
import zoe_web.web.status
import zoe_web.web.applications

from zoe_lib.version import ZOE_API_VERSION, ZOE_VERSION

@web_bp.context_processor
def inject_version():
    return {
        'zoe_version': ZOE_VERSION,
        'zoe_api_version': ZOE_API_VERSION,
    }
<|endoftext|>


<commit_before>#!/usr/bin/python

## Binary Analysis Tool
## Copyright 2009-2013 Armijn Hemel for Tjaldur Software Governance Solutions
## Licensed under Apache 2.0, see LICENSE file for details

'''This file contains information about how to recognize certain
files, file systems, compression, and so on automatically and which
methods or functions to invoke to unpack these files for further
analysis.'''

## information from:
## 1. /usr/share/magic
## 2. include/linux/magic.h in the Linux kernel sources
## 3. http://www.squashfs-lzma.org/
## 4. http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=364260
## 5. various other places

## This is not the same as the magic database, but just a list of
## identifiers that are used for these file systems, compressed files,etc.
## In BAT a lot more work is done to verify what a file really is, which
## the magic database does not do.

fsmagic = {
            'gzip':             '\x1f\x8b\x08',     # x08 is the only compression method according to RFC 1952
            'compress':         '\x1f\x9d',
            'bz2':              'BZh',
            'rar':              'Rar!\x1a\x07',
            'zip':              '\x50\x4b\x03\04',
            'zipend':           '\x50\x4b\x05\06',
            'lrzip':            'LRZI',
            'rzip':             'RZIP',
            'squashfs1':        '\x68\x73\x71\x73', # hsqs -- little endian
            'squashfs2':        '\x73\x71\x73\x68', # sqsh -- big endian
            'squashfs3':        '\x71\x73\x68\x73', # qshs -- little endian
            'squashfs4':        '\x73\x68\x73\x71', # shsq -- big endian
            'squashfs5':        '\x74\x71\x73\x68', # tqsh - used in DD-WRT
            'squashfs6':        '\x68\x73\x71\x74', # hsqt - used in DD-WRT
            'squashfs7':        '\x73\x71\x6c\x7a', # sqlz
            'android-sparse':   '\x3a\xff\x26\xed',
            'lzma_alone':       '\x5d\x00\x00',
            'lzma_alone_alt':   '\x6d\x00\x00',     # used in OpenWrt
            'lzma_alone_alt2':  '\x6c\x00\x00',     # seen in some routers, like ZyXEL NBG5615
            '7z':               '7z\xbc\xaf\x27\x1c',
            'xz':               '\xfd\x37\x7a\x58\x5a\x00',
            'xztrailer':        '\x59\x5a',
            'lzip':             'LZIP',
            'lzo':              '\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a',
            'cramfs_le':        '\x45\x3d\xcd\x28',
            'cramfs_be':        '\x28\xcd\x3d\x45',
            'romfs':            '-rom1fs-',
            'jffs2_le':         '\x85\x19',
            'jffs2_be':         '\x19\x85',
            'ubifs':            '\x31\x18\x10\x06',
            'ubi':              '\x55\x42\x49\x23',
            'rpm':              '\xed\xab\xee\xdb',
            'ext2':             '\x53\xef',        # little endian
            'minix':            '\x8f\x13',        # specific version of Minix v1 file system
            'arj':              '\x60\xea',
            'cab':              'MSCF\x00\x00\x00\x00',    # first four bytes following header are always 0
            'installshield':    'ISc(',
            'pkbac':            'PKBAC',
            'winrar':           'WinRAR',
            'png':              '\x89PNG\x0d\x0a\x1a\x0a',
            'pngtrailer':       'IEND',
            'cpiotrailer':      'TRAILER!!!',
            'bmp':              'BM',
            'jpeg':             '\xff\xd8',
            'jpegtrailer':      '\xff\xd9',
            'jfif':             'JFIF',
            'gif87':            'GIF87a',
            'gif89':            'GIF89a',
            'ico':              '\x00\x00\x01\x00',
            'cpio1':            '070701',
            'cpio2':            '070702',
            'cpio3':            '070707',
            'iso9660':          'CD001',
            'swf':              'CWS',
            'pdf':              '%PDF-',
            'pdftrailer':       '%%EOF',
            'ar':               '!<arch>',
            'tar1':             'ustar\x00',
            'tar2':             'ustar\x20',
            'java_serialized':  '\xac\xed\x00',
            'ogg':  		'OggS',
            'fat12':  		'FAT12',
            'fat16':  		'FAT16',
            'pe':  		'MZ',
            'upx':  		'UPX',
            'java': 		'\xca\xfe\xba\xbe',
            'pack200':		'\xca\xfe\xd0\x0d',
            'dex':		'dex\n',
            'odex':		'dey\n',
            'otf':		'OTTO',
            'mp4':		'ftyp',
            'sqlite3':		'SQLite format 3\x00',
            'u-boot':		'\x27\x05\x19\x56',
            'yaffs2':		'\x03\x00\x00\x00\x01\x00\x00\x00\xff\xff', ## this is not a an official signature, just occuring frequently
            'plf':		'\x50\x4c\x46\x21',
            'chm':		'ITSF\x03\x00\x00\x00\x60\x00\x00\x00\x01\x00\x00\x00',
            'msi':		'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1', ## not sure this is a correct signature
            'windowsassemblyheader':		'<assembly',
            'windowsassemblytrailer':		'</assembly>',
          }

## some offsets can be found after a certain number of bytes, but
## the actual file system or file starts earlier
correction = {
               'ext2':    0x438,
               'minix':   0x410,
               'iso9660': 32769,
               'tar1':    0x101,
               'tar2':    0x101,
               'fat12':   54,
               'fat16':   54,
             }

## collection of markers that should be scanned together
squashtypes = ['squashfs1', 'squashfs2', 'squashfs3', 'squashfs4', 'squashfs5', 'squashfs6']
lzmatypes   = ['lzma_alone', 'lzma_alone_alt', 'lzma_alone_alt2']
cpio        = ['cpio1', 'cpio2', 'cpio3']
gif         = ['gif87', 'gif89']
tar         = ['tar1', 'tar2']
<commit_msg>add magic for optional RAR terminator block<commit_after>#!/usr/bin/python

## Binary Analysis Tool
## Copyright 2009-2013 Armijn Hemel for Tjaldur Software Governance Solutions
## Licensed under Apache 2.0, see LICENSE file for details

'''This file contains information about how to recognize certain
files, file systems, compression, and so on automatically and which
methods or functions to invoke to unpack these files for further
analysis.'''

## information from:
## 1. /usr/share/magic
## 2. include/linux/magic.h in the Linux kernel sources
## 3. http://www.squashfs-lzma.org/
## 4. http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=364260
## 5. various other places

## This is not the same as the magic database, but just a list of
## identifiers that are used for these file systems, compressed files,etc.
## In BAT a lot more work is done to verify what a file really is, which
## the magic database does not do.

fsmagic = {
            'gzip':             '\x1f\x8b\x08',     # x08 is the only compression method according to RFC 1952
            'compress':         '\x1f\x9d',
            'bz2':              'BZh',
            'rar':              'Rar!\x1a\x07',
            'rarfooter':        '\xc4\x3d\x7b\x00\x40\x07\x00', # http://forensicswiki.org/wiki/RAR#Terminator_.28terminator.29
            'zip':              '\x50\x4b\x03\04',
            'zipend':           '\x50\x4b\x05\06',
            'lrzip':            'LRZI',
            'rzip':             'RZIP',
            'squashfs1':        '\x68\x73\x71\x73', # hsqs -- little endian
            'squashfs2':        '\x73\x71\x73\x68', # sqsh -- big endian
            'squashfs3':        '\x71\x73\x68\x73', # qshs -- little endian
            'squashfs4':        '\x73\x68\x73\x71', # shsq -- big endian
            'squashfs5':        '\x74\x71\x73\x68', # tqsh - used in DD-WRT
            'squashfs6':        '\x68\x73\x71\x74', # hsqt - used in DD-WRT
            'squashfs7':        '\x73\x71\x6c\x7a', # sqlz
            'android-sparse':   '\x3a\xff\x26\xed',
            'lzma_alone':       '\x5d\x00\x00',
            'lzma_alone_alt':   '\x6d\x00\x00',     # used in OpenWrt
            'lzma_alone_alt2':  '\x6c\x00\x00',     # seen in some routers, like ZyXEL NBG5615
            '7z':               '7z\xbc\xaf\x27\x1c',
            'xz':               '\xfd\x37\x7a\x58\x5a\x00',
            'xztrailer':        '\x59\x5a',
            'lzip':             'LZIP',
            'lzo':              '\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a',
            'cramfs_le':        '\x45\x3d\xcd\x28',
            'cramfs_be':        '\x28\xcd\x3d\x45',
            'romfs':            '-rom1fs-',
            'jffs2_le':         '\x85\x19',
            'jffs2_be':         '\x19\x85',
            'ubifs':            '\x31\x18\x10\x06',
            'ubi':              '\x55\x42\x49\x23',
            'rpm':              '\xed\xab\xee\xdb',
            'ext2':             '\x53\xef',        # little endian
            'minix':            '\x8f\x13',        # specific version of Minix v1 file system
            'arj':              '\x60\xea',
            'cab':              'MSCF\x00\x00\x00\x00',    # first four bytes following header are always 0
            'installshield':    'ISc(',
            'pkbac':            'PKBAC',
            'winrar':           'WinRAR',
            'png':              '\x89PNG\x0d\x0a\x1a\x0a',
            'pngtrailer':       'IEND',
            'cpiotrailer':      'TRAILER!!!',
            'bmp':              'BM',
            'jpeg':             '\xff\xd8',
            'jpegtrailer':      '\xff\xd9',
            'jfif':             'JFIF',
            'gif87':            'GIF87a',
            'gif89':            'GIF89a',
            'ico':              '\x00\x00\x01\x00',
            'cpio1':            '070701',
            'cpio2':            '070702',
            'cpio3':            '070707',
            'iso9660':          'CD001',
            'swf':              'CWS',
            'pdf':              '%PDF-',
            'pdftrailer':       '%%EOF',
            'ar':               '!<arch>',
            'tar1':             'ustar\x00',
            'tar2':             'ustar\x20',
            'java_serialized':  '\xac\xed\x00',
            'ogg':  		'OggS',
            'fat12':  		'FAT12',
            'fat16':  		'FAT16',
            'pe':  		'MZ',
            'upx':  		'UPX',
            'java': 		'\xca\xfe\xba\xbe',
            'pack200':		'\xca\xfe\xd0\x0d',
            'dex':		'dex\n',
            'odex':		'dey\n',
            'otf':		'OTTO',
            'mp4':		'ftyp',
            'sqlite3':		'SQLite format 3\x00',
            'u-boot':		'\x27\x05\x19\x56',
            'yaffs2':		'\x03\x00\x00\x00\x01\x00\x00\x00\xff\xff', ## this is not a an official signature, just occuring frequently
            'plf':		'\x50\x4c\x46\x21',
            'chm':		'ITSF\x03\x00\x00\x00\x60\x00\x00\x00\x01\x00\x00\x00',
            'msi':		'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1', ## not sure this is a correct signature
            'windowsassemblyheader':		'<assembly',
            'windowsassemblytrailer':		'</assembly>',
          }

## some offsets can be found after a certain number of bytes, but
## the actual file system or file starts earlier
correction = {
               'ext2':    0x438,
               'minix':   0x410,
               'iso9660': 32769,
               'tar1':    0x101,
               'tar2':    0x101,
               'fat12':   54,
               'fat16':   54,
             }

## collection of markers that should be scanned together
squashtypes = ['squashfs1', 'squashfs2', 'squashfs3', 'squashfs4', 'squashfs5', 'squashfs6']
lzmatypes   = ['lzma_alone', 'lzma_alone_alt', 'lzma_alone_alt2']
cpio        = ['cpio1', 'cpio2', 'cpio3']
gif         = ['gif87', 'gif89']
tar         = ['tar1', 'tar2']
<|endoftext|>


<commit_before>from optparse import make_option
from django.core.management import BaseCommand
from corehq.apps.change_feed.consumer.feed import KafkaChangeFeed
from corehq.apps.change_feed.exceptions import UnavailableKafkaOffset
from corehq.apps.change_feed.topics import validate_offsets
from pillowtop import get_all_pillow_instances

class Command(BaseCommand):
    help = ("Validates that all pillows that use kafka have checkpoints that still exist "
            "in the kafka feed.")

    option_list = (
        make_option('--print-only',
                    action='store_true',
                    dest='print_only',
                    default=False,
                    help="Only print information, don't fail if checkpoints aren't valid."),
    )

    def handle(self, *args, **options):
        print_only = options['print_only']
        validate_checkpoints(print_only)

def validate_checkpoints(print_only):

    for pillow in get_all_pillow_instances():
        if isinstance(pillow.get_change_feed(), KafkaChangeFeed):
            checkpoint_dict = _get_checkpoint_dict(pillow)
            try:
                validate_offsets(checkpoint_dict)
            except UnavailableKafkaOffset as e:
                message = u'Problem with checkpoint for {}: {}'.format(
                    pillow.pillow_id, e
                )
                if print_only:
                    print message
                else:
                    raise Exception(message)

def _get_checkpoint_dict(pillow):
    sequence = pillow.get_last_checkpoint_sequence()
    if isinstance(sequence, dict):
        sequence_dict = sequence
    else:
        try:
            sequence_int = int(sequence)
        except ValueError:
            # assume this is an old/legacy checkpoint
            return {}
        else:
            sequence_dict = {
                pillow.get_change_feed()._get_single_topic_or_fail(): sequence_int
            }
    # filter out 0's since we don't want to check those as they are likely new pillows
    return {
        k: v for k, v in sequence_dict.items() if v > 0
    }
<commit_msg>use add_arguments<commit_after>from optparse import make_option
from django.core.management import BaseCommand
from corehq.apps.change_feed.consumer.feed import KafkaChangeFeed
from corehq.apps.change_feed.exceptions import UnavailableKafkaOffset
from corehq.apps.change_feed.topics import validate_offsets
from pillowtop import get_all_pillow_instances

class Command(BaseCommand):
    help = ("Validates that all pillows that use kafka have checkpoints that still exist "
            "in the kafka feed.")

    def add_arguments(self, parser):
        parser.add_argument(
            '--print-only',
            action='store_true',
            dest='print_only',
            default=False,
            help="Only print information, don't fail if checkpoints aren't valid.",
        )

    def handle(self, **options):
        print_only = options['print_only']
        validate_checkpoints(print_only)

def validate_checkpoints(print_only):

    for pillow in get_all_pillow_instances():
        if isinstance(pillow.get_change_feed(), KafkaChangeFeed):
            checkpoint_dict = _get_checkpoint_dict(pillow)
            try:
                validate_offsets(checkpoint_dict)
            except UnavailableKafkaOffset as e:
                message = u'Problem with checkpoint for {}: {}'.format(
                    pillow.pillow_id, e
                )
                if print_only:
                    print message
                else:
                    raise Exception(message)

def _get_checkpoint_dict(pillow):
    sequence = pillow.get_last_checkpoint_sequence()
    if isinstance(sequence, dict):
        sequence_dict = sequence
    else:
        try:
            sequence_int = int(sequence)
        except ValueError:
            # assume this is an old/legacy checkpoint
            return {}
        else:
            sequence_dict = {
                pillow.get_change_feed()._get_single_topic_or_fail(): sequence_int
            }
    # filter out 0's since we don't want to check those as they are likely new pillows
    return {
        k: v for k, v in sequence_dict.items() if v > 0
    }
<|endoftext|>


<commit_before>#!/usr/bin/env python
"""
Convert all recipes into feedstocks.

This script is to be run in a TravisCI context, with all secret environment variables defined (BINSTAR_TOKEN, GH_TOKEN)
Such as:

    export GH_TOKEN=$(cat ~/.conda-smithy/github.token)

"""
from __future__ import print_function

from conda_build.metadata import MetaData
from conda_smithy.github import gh_token
from contextlib import contextmanager
from datetime import datetime
from github import Github, GithubException, Team
import os.path
from random import choice
import shutil
import subprocess
import sys
import tempfile
import traceback

# Enable DEBUG to run the diagnostics, without actually creating new feedstocks.
DEBUG = False

superlative = ['awesome', 'slick', 'formidable', 'awe-inspiring', 'breathtaking',
               'magnificent', 'wonderous', 'stunning', 'astonishing', 'superb',
               'splendid', 'impressive', 'unbeatable', 'excellent', 'top', 'outstanding',
               'exalted', 'standout', 'smashing']

recipe_directory_name = 'recipes'
def list_recipes():
    if os.path.isdir(recipe_directory_name):
        recipes = os.listdir(recipe_directory_name)
    else:
        recipes = []

    for recipe_dir in recipes:
        # We don't list the "example" feedstock. It is an example, and is there
        # to be helpful.
        if recipe_dir.startswith('example'):
            continue
        path = os.path.abspath(os.path.join(recipe_directory_name, recipe_dir))
        yield path, recipe_dir

@contextmanager
def tmp_dir(*args, **kwargs):
    temp_dir = tempfile.mkdtemp(*args, **kwargs)
    try:
        yield temp_dir
    finally:
        shutil.rmtree(temp_dir)

def repo_exists(organization, name):
    token = gh_token()
    gh = Github(token)
    # Use the organization provided.
    org = gh.get_organization(organization)
    try:
        org.get_repo(name)
        return True
    except GithubException as e:
        if e.status == 404:
            return False
        raise

def create_team(org, name, description, repo_names):
    # PyGithub creates secret teams, and has no way of turning that off! :(
    post_parameters = {
        "name": name,
        "description": description,
        "privacy": "closed",
        "permission": "push",
        "repo_names": repo_names
    }
    headers, data = org._requester.requestJsonAndCheck(
        "POST",
        org.url + "/teams",
        input=post_parameters
    )
    return Team.Team(org._requester, headers, data, completed=True)

def print_rate_limiting_info(gh):
    # Compute some info about our GitHub API Rate Limit.
    # Note that it doesn't count against our limit to
    # get this info. So, we should be doing this regularly
    # to better know when it is going to run out. Also,
    # this will help us better understand where we are
    # spending it and how to better optimize it.

    # Get GitHub API Rate Limit usage and total
    gh_api_remaining, gh_api_total = gh.rate_limiting

    # Compute time until GitHub API Rate Limit reset
    gh_api_reset_time = gh.rate_limiting_resettime
    gh_api_reset_time = datetime.utcfromtimestamp(gh_api_reset_time)
    gh_api_reset_time -= datetime.utcnow()

    print("")
    print("GitHub API Rate Limit Info:")
    print("---------------------------")
    print("Currently remaining {remaining} out of {total}.".format(remaining=gh_api_remaining, total=gh_api_total))
    print("Will reset in {time}.".format(time=gh_api_reset_time))
    print("")

if __name__ == '__main__':
    exit_code = 0

    is_merged_pr = (os.environ.get('TRAVIS_BRANCH') == 'master' and os.environ.get('TRAVIS_PULL_REQUEST') == 'false')

    smithy_conf = os.path.expanduser('~/.conda-smithy')
    if not os.path.exists(smithy_conf):
        os.mkdir(smithy_conf)

    def write_token(name, token):
        with open(os.path.join(smithy_conf, name + '.token'), 'w') as fh:
            fh.write(token)
    if 'APPVEYOR_TOKEN' in os.environ:
        write_token('appveyor', os.environ['APPVEYOR_TOKEN'])
    if 'CIRCLE_TOKEN' in os.environ:
        write_token('circle', os.environ['CIRCLE_TOKEN'])
    gh = None
    if 'GH_TOKEN' in os.environ:
        write_token('github', os.environ['GH_TOKEN'])
        gh = Github(os.environ['GH_TOKEN'])

        # Get our initial rate limit info.
        print_rate_limiting_info(gh)

    owner_info = ['--organization', 'conda-forge']

    print('Calculating the recipes which need to be turned into feedstocks.')
    with tmp_dir('__feedstocks') as feedstocks_dir:
        feedstock_dirs = []
        for recipe_dir, name in list_recipes():
            feedstock_dir = os.path.join(feedstocks_dir, name + '-feedstock')
            os.mkdir(feedstock_dir)
            print('Making feedstock for {}'.format(name))

            subprocess.check_call(['conda', 'smithy', 'init', recipe_dir,
                                   '--feedstock-directory', feedstock_dir])
            if not is_merged_pr:
                # We just want to check that conda-smithy is doing its thing without having any metadata issues.
                continue

            feedstock_dirs.append([feedstock_dir, name, recipe_dir])

            subprocess.check_call(['git', 'remote', 'add', 'upstream_with_token',
                                   'https://conda-forge-manager:{}@github.com/conda-forge/{}'.format(os.environ['GH_TOKEN'],
                                                                                os.path.basename(feedstock_dir))],
                                  cwd=feedstock_dir)

            # Sometimes we already have the feedstock created. We need to deal with that case.
            if repo_exists('conda-forge', os.path.basename(feedstock_dir)):
                subprocess.check_call(['git', 'fetch', 'upstream_with_token'], cwd=feedstock_dir)
                subprocess.check_call(['git', 'branch', '-m', 'master', 'old'], cwd=feedstock_dir)
                try:
                    subprocess.check_call(['git', 'checkout', '-b', 'master', 'upstream_with_token/master'], cwd=feedstock_dir)
                except subprocess.CalledProcessError:
                    # Sometimes, we have a repo, but there are no commits on it! Just catch that case.
                    subprocess.check_call(['git', 'checkout', '-b' 'master'], cwd=feedstock_dir)
            else:
                subprocess.check_call(['conda', 'smithy', 'register-github', feedstock_dir] + owner_info)

        conda_forge = None
        teams = None
        if gh:
            # Only get the org and teams if there is stuff to add.
            if feedstock_dirs:
                conda_forge = gh.get_organization('conda-forge')
                teams = {team.name: team for team in conda_forge.get_teams()}

        # Break the previous loop to allow the TravisCI registering to take place only once per function call.
        # Without this, intermittent failures to synch the TravisCI repos ensue.
        all_maintainers = set()
        # Hang on to any CI registration errors that occur and raise them at the end.
        for feedstock_dir, name, recipe_dir in feedstock_dirs:
            # Try to register each feedstock with CI.
            # However sometimes their APIs have issues for whatever reason.
            # In order to bank our progress, we note the error and handle it.
            # After going through all the recipes and removing the converted ones,
            # we fail the build so that people are aware that things did not clear.
            try:
                subprocess.check_call(['conda', 'smithy', 'register-ci', '--feedstock_directory', feedstock_dir] + owner_info)
            except subprocess.CalledProcessError:
                exit_code = 1
                traceback.print_exception(*sys.exc_info())
                continue

            subprocess.check_call(['conda', 'smithy', 'rerender'], cwd=feedstock_dir)
            subprocess.check_call(['git', 'commit', '-am', "Re-render the feedstock after CI registration."], cwd=feedstock_dir)
            for i in range(5):
                try:
                    # Capture the output, as it may contain the GH_TOKEN.
                    out = subprocess.check_output(['git', 'push', 'upstream_with_token', 'master'], cwd=feedstock_dir,
                                                  stderr=subprocess.STDOUT)
                    break
                except subprocess.CalledProcessError:
                    pass

                # Likely another job has already pushed to this repo.
                # Place our changes on top of theirs and try again.
                out = subprocess.check_output(['git', 'fetch', 'upstream_with_token', 'master'], cwd=feedstock_dir,
                                              stderr=subprocess.STDOUT)
                try:
                    subprocess.check_call(['git', 'rebase', 'upstream_with_token/master', 'master'], cwd=feedstock_dir)
                except subprocess.CalledProcessError:
                    # Handle rebase failure by choosing the changes in `master`.
                    subprocess.check_call(['git', 'checkout', 'master', '--', '.'], cwd=feedstock_dir)
                    subprocess.check_call(['git', 'rebase', '--continue'], cwd=feedstock_dir)

            # Add team members as maintainers.
            if conda_forge:
                meta = MetaData(recipe_dir)
                maintainers = set(meta.meta.get('extra', {}).get('recipe-maintainers', []))
                all_maintainers.update(maintainers)
                team_name = name.lower()
                repo_name = 'conda-forge/{}'.format(os.path.basename(feedstock_dir))

                # Try to get team or create it if it doesn't exist.
                team = teams.get(team_name)
                if not team:
                    team = create_team(
                        conda_forge,
                        team_name,
                        'The {} {} contributors!'.format(choice(superlative), team_name),
                        repo_names=[repo_name]
                    )
                    teams[team_name] = team
                    current_maintainers = []
                else:
                    current_maintainers = team.get_members()

                # Add only the new maintainers to the team.
                current_maintainers_handles = set([each_maintainers.login.lower() for each_maintainers in current_maintainers])
                for new_maintainer in maintainers - current_maintainers_handles:
                    headers, data = team._requester.requestJsonAndCheck(
                        "PUT",
                        team.url + "/memberships/" + new_maintainer
                    )
                # Mention any maintainers that need to be removed (unlikely here).
                for old_maintainer in current_maintainers_handles - maintainers:
                    print("AN OLD MEMBER ({}) NEEDS TO BE REMOVED FROM {}".format(old_maintainer, repo_name))

            # Remove this recipe from the repo.
            if is_merged_pr:
                subprocess.check_call(['git', 'rm', '-r', recipe_dir])

    # Add new conda-forge members to all-members team. Welcome! :)
    if conda_forge:
        team_name = 'all-members'
        team = teams.get(team_name)
        if not team:
            team = create_team(
                conda_forge,
                team_name,
                'All of the awesome conda-forge contributors!',
                []
            )
            teams[team_name] = team
            current_members = []
        else:
            current_members = team.get_members()

        # Add only the new members to the team.
        current_members_handles = set([each_member.login.lower() for each_member in current_members])
        for new_member in all_maintainers - current_members_handles:
            print("Adding a new member ({}) to conda-forge. Welcome! :)".format(new_member))
            headers, data = team._requester.requestJsonAndCheck(
                "PUT",
                team.url + "/memberships/" + new_member
            )

    # Update status based on the remote.
    subprocess.check_call(['git', 'stash', '--keep-index', '--include-untracked'])
    subprocess.check_call(['git', 'fetch'])
    subprocess.check_call(['git', 'rebase', '--autostash'])
    subprocess.check_call(['git', 'add', '.'])
    try:
        subprocess.check_call(['git', 'stash', 'pop'])
    except subprocess.CalledProcessError:
        # In case there was nothing to stash.
        # Finish quietly.
        pass

    # Parse `git status --porcelain` to handle some merge conflicts and generate the removed recipe list.
    changed_files = subprocess.check_output(['git', 'status', '--porcelain', recipe_directory_name],
                                            universal_newlines=True)
    changed_files = changed_files.splitlines()

    # Add all files from AU conflicts. They are new files that we weren't tracking previously.
    # Adding them resolves the conflict and doesn't actually add anything to the index.
    new_file_conflicts = filter(lambda _: _.startswith("AU "), changed_files)
    new_file_conflicts = map(lambda _ : _.replace("AU", "", 1).lstrip(), new_file_conflicts)
    for each_new_file in new_file_conflicts:
        subprocess.check_call(['git', 'add', each_new_file])

    # Generate a fresh listing of recipes removed.
    #
    # * Each line we get back is a change to a file in the recipe directory.
    # * We narrow the list down to recipes that are staged for deletion (ignores examples).
    # * Then we clean up the list so that it only has the recipe names.
    removed_recipes = filter(lambda _: _.startswith("D "), changed_files)
    removed_recipes = map(lambda _ : _.replace("D", "", 1).lstrip(), removed_recipes)
    removed_recipes = map(lambda _ : os.path.relpath(_, recipe_directory_name), removed_recipes)
    removed_recipes = map(lambda _ : _.split(os.path.sep)[0], removed_recipes)
    removed_recipes = sorted(set(removed_recipes))

    # Commit any removed packages.
    subprocess.check_call(['git', 'status'])
    if removed_recipes:
        msg = ('Removed recipe{s} ({}) after converting into feedstock{s}.'
               ''.format(', '.join(removed_recipes),
                         s=('s' if len(removed_recipes) > 1 else '')))
        msg += ' [ci skip]' if exit_code == 0 else ' [skip appveyor]'
        if is_merged_pr:
            # Capture the output, as it may contain the GH_TOKEN.
            out = subprocess.check_output(['git', 'remote', 'add', 'upstream_with_token',
                                           'https://conda-forge-manager:{}@github.com/conda-forge/staged-recipes'.format(os.environ['GH_TOKEN'])],
                                          stderr=subprocess.STDOUT)
            subprocess.check_call(['git', 'commit', '-m', msg])
            # Capture the output, as it may contain the GH_TOKEN.
            out = subprocess.check_output(['git', 'push', 'upstream_with_token', os.environ.get('TRAVIS_BRANCH')],
                                          stderr=subprocess.STDOUT)
        else:
            print('Would git commit, with the following message: \n   {}'.format(msg))

    if gh:
        # Get our final rate limit info.
        print_rate_limiting_info(gh)

    sys.exit(exit_code)
<commit_msg>Drop preemptive creation of feedstock directory<commit_after>#!/usr/bin/env python
"""
Convert all recipes into feedstocks.

This script is to be run in a TravisCI context, with all secret environment variables defined (BINSTAR_TOKEN, GH_TOKEN)
Such as:

    export GH_TOKEN=$(cat ~/.conda-smithy/github.token)

"""
from __future__ import print_function

from conda_build.metadata import MetaData
from conda_smithy.github import gh_token
from contextlib import contextmanager
from datetime import datetime
from github import Github, GithubException, Team
import os.path
from random import choice
import shutil
import subprocess
import sys
import tempfile
import traceback

# Enable DEBUG to run the diagnostics, without actually creating new feedstocks.
DEBUG = False

superlative = ['awesome', 'slick', 'formidable', 'awe-inspiring', 'breathtaking',
               'magnificent', 'wonderous', 'stunning', 'astonishing', 'superb',
               'splendid', 'impressive', 'unbeatable', 'excellent', 'top', 'outstanding',
               'exalted', 'standout', 'smashing']

recipe_directory_name = 'recipes'
def list_recipes():
    if os.path.isdir(recipe_directory_name):
        recipes = os.listdir(recipe_directory_name)
    else:
        recipes = []

    for recipe_dir in recipes:
        # We don't list the "example" feedstock. It is an example, and is there
        # to be helpful.
        if recipe_dir.startswith('example'):
            continue
        path = os.path.abspath(os.path.join(recipe_directory_name, recipe_dir))
        yield path, recipe_dir

@contextmanager
def tmp_dir(*args, **kwargs):
    temp_dir = tempfile.mkdtemp(*args, **kwargs)
    try:
        yield temp_dir
    finally:
        shutil.rmtree(temp_dir)

def repo_exists(organization, name):
    token = gh_token()
    gh = Github(token)
    # Use the organization provided.
    org = gh.get_organization(organization)
    try:
        org.get_repo(name)
        return True
    except GithubException as e:
        if e.status == 404:
            return False
        raise

def create_team(org, name, description, repo_names):
    # PyGithub creates secret teams, and has no way of turning that off! :(
    post_parameters = {
        "name": name,
        "description": description,
        "privacy": "closed",
        "permission": "push",
        "repo_names": repo_names
    }
    headers, data = org._requester.requestJsonAndCheck(
        "POST",
        org.url + "/teams",
        input=post_parameters
    )
    return Team.Team(org._requester, headers, data, completed=True)

def print_rate_limiting_info(gh):
    # Compute some info about our GitHub API Rate Limit.
    # Note that it doesn't count against our limit to
    # get this info. So, we should be doing this regularly
    # to better know when it is going to run out. Also,
    # this will help us better understand where we are
    # spending it and how to better optimize it.

    # Get GitHub API Rate Limit usage and total
    gh_api_remaining, gh_api_total = gh.rate_limiting

    # Compute time until GitHub API Rate Limit reset
    gh_api_reset_time = gh.rate_limiting_resettime
    gh_api_reset_time = datetime.utcfromtimestamp(gh_api_reset_time)
    gh_api_reset_time -= datetime.utcnow()

    print("")
    print("GitHub API Rate Limit Info:")
    print("---------------------------")
    print("Currently remaining {remaining} out of {total}.".format(remaining=gh_api_remaining, total=gh_api_total))
    print("Will reset in {time}.".format(time=gh_api_reset_time))
    print("")

if __name__ == '__main__':
    exit_code = 0

    is_merged_pr = (os.environ.get('TRAVIS_BRANCH') == 'master' and os.environ.get('TRAVIS_PULL_REQUEST') == 'false')

    smithy_conf = os.path.expanduser('~/.conda-smithy')
    if not os.path.exists(smithy_conf):
        os.mkdir(smithy_conf)

    def write_token(name, token):
        with open(os.path.join(smithy_conf, name + '.token'), 'w') as fh:
            fh.write(token)
    if 'APPVEYOR_TOKEN' in os.environ:
        write_token('appveyor', os.environ['APPVEYOR_TOKEN'])
    if 'CIRCLE_TOKEN' in os.environ:
        write_token('circle', os.environ['CIRCLE_TOKEN'])
    gh = None
    if 'GH_TOKEN' in os.environ:
        write_token('github', os.environ['GH_TOKEN'])
        gh = Github(os.environ['GH_TOKEN'])

        # Get our initial rate limit info.
        print_rate_limiting_info(gh)

    owner_info = ['--organization', 'conda-forge']

    print('Calculating the recipes which need to be turned into feedstocks.')
    with tmp_dir('__feedstocks') as feedstocks_dir:
        feedstock_dirs = []
        for recipe_dir, name in list_recipes():
            feedstock_dir = os.path.join(feedstocks_dir, name + '-feedstock')
            print('Making feedstock for {}'.format(name))

            subprocess.check_call(['conda', 'smithy', 'init', recipe_dir,
                                   '--feedstock-directory', feedstock_dir])
            if not is_merged_pr:
                # We just want to check that conda-smithy is doing its thing without having any metadata issues.
                continue

            feedstock_dirs.append([feedstock_dir, name, recipe_dir])

            subprocess.check_call(['git', 'remote', 'add', 'upstream_with_token',
                                   'https://conda-forge-manager:{}@github.com/conda-forge/{}'.format(os.environ['GH_TOKEN'],
                                                                                os.path.basename(feedstock_dir))],
                                  cwd=feedstock_dir)

            # Sometimes we already have the feedstock created. We need to deal with that case.
            if repo_exists('conda-forge', os.path.basename(feedstock_dir)):
                subprocess.check_call(['git', 'fetch', 'upstream_with_token'], cwd=feedstock_dir)
                subprocess.check_call(['git', 'branch', '-m', 'master', 'old'], cwd=feedstock_dir)
                try:
                    subprocess.check_call(['git', 'checkout', '-b', 'master', 'upstream_with_token/master'], cwd=feedstock_dir)
                except subprocess.CalledProcessError:
                    # Sometimes, we have a repo, but there are no commits on it! Just catch that case.
                    subprocess.check_call(['git', 'checkout', '-b' 'master'], cwd=feedstock_dir)
            else:
                subprocess.check_call(['conda', 'smithy', 'register-github', feedstock_dir] + owner_info)

        conda_forge = None
        teams = None
        if gh:
            # Only get the org and teams if there is stuff to add.
            if feedstock_dirs:
                conda_forge = gh.get_organization('conda-forge')
                teams = {team.name: team for team in conda_forge.get_teams()}

        # Break the previous loop to allow the TravisCI registering to take place only once per function call.
        # Without this, intermittent failures to synch the TravisCI repos ensue.
        all_maintainers = set()
        # Hang on to any CI registration errors that occur and raise them at the end.
        for feedstock_dir, name, recipe_dir in feedstock_dirs:
            # Try to register each feedstock with CI.
            # However sometimes their APIs have issues for whatever reason.
            # In order to bank our progress, we note the error and handle it.
            # After going through all the recipes and removing the converted ones,
            # we fail the build so that people are aware that things did not clear.
            try:
                subprocess.check_call(['conda', 'smithy', 'register-ci', '--feedstock_directory', feedstock_dir] + owner_info)
            except subprocess.CalledProcessError:
                exit_code = 1
                traceback.print_exception(*sys.exc_info())
                continue

            subprocess.check_call(['conda', 'smithy', 'rerender'], cwd=feedstock_dir)
            subprocess.check_call(['git', 'commit', '-am', "Re-render the feedstock after CI registration."], cwd=feedstock_dir)
            for i in range(5):
                try:
                    # Capture the output, as it may contain the GH_TOKEN.
                    out = subprocess.check_output(['git', 'push', 'upstream_with_token', 'master'], cwd=feedstock_dir,
                                                  stderr=subprocess.STDOUT)
                    break
                except subprocess.CalledProcessError:
                    pass

                # Likely another job has already pushed to this repo.
                # Place our changes on top of theirs and try again.
                out = subprocess.check_output(['git', 'fetch', 'upstream_with_token', 'master'], cwd=feedstock_dir,
                                              stderr=subprocess.STDOUT)
                try:
                    subprocess.check_call(['git', 'rebase', 'upstream_with_token/master', 'master'], cwd=feedstock_dir)
                except subprocess.CalledProcessError:
                    # Handle rebase failure by choosing the changes in `master`.
                    subprocess.check_call(['git', 'checkout', 'master', '--', '.'], cwd=feedstock_dir)
                    subprocess.check_call(['git', 'rebase', '--continue'], cwd=feedstock_dir)

            # Add team members as maintainers.
            if conda_forge:
                meta = MetaData(recipe_dir)
                maintainers = set(meta.meta.get('extra', {}).get('recipe-maintainers', []))
                all_maintainers.update(maintainers)
                team_name = name.lower()
                repo_name = 'conda-forge/{}'.format(os.path.basename(feedstock_dir))

                # Try to get team or create it if it doesn't exist.
                team = teams.get(team_name)
                if not team:
                    team = create_team(
                        conda_forge,
                        team_name,
                        'The {} {} contributors!'.format(choice(superlative), team_name),
                        repo_names=[repo_name]
                    )
                    teams[team_name] = team
                    current_maintainers = []
                else:
                    current_maintainers = team.get_members()

                # Add only the new maintainers to the team.
                current_maintainers_handles = set([each_maintainers.login.lower() for each_maintainers in current_maintainers])
                for new_maintainer in maintainers - current_maintainers_handles:
                    headers, data = team._requester.requestJsonAndCheck(
                        "PUT",
                        team.url + "/memberships/" + new_maintainer
                    )
                # Mention any maintainers that need to be removed (unlikely here).
                for old_maintainer in current_maintainers_handles - maintainers:
                    print("AN OLD MEMBER ({}) NEEDS TO BE REMOVED FROM {}".format(old_maintainer, repo_name))

            # Remove this recipe from the repo.
            if is_merged_pr:
                subprocess.check_call(['git', 'rm', '-r', recipe_dir])

    # Add new conda-forge members to all-members team. Welcome! :)
    if conda_forge:
        team_name = 'all-members'
        team = teams.get(team_name)
        if not team:
            team = create_team(
                conda_forge,
                team_name,
                'All of the awesome conda-forge contributors!',
                []
            )
            teams[team_name] = team
            current_members = []
        else:
            current_members = team.get_members()

        # Add only the new members to the team.
        current_members_handles = set([each_member.login.lower() for each_member in current_members])
        for new_member in all_maintainers - current_members_handles:
            print("Adding a new member ({}) to conda-forge. Welcome! :)".format(new_member))
            headers, data = team._requester.requestJsonAndCheck(
                "PUT",
                team.url + "/memberships/" + new_member
            )

    # Update status based on the remote.
    subprocess.check_call(['git', 'stash', '--keep-index', '--include-untracked'])
    subprocess.check_call(['git', 'fetch'])
    subprocess.check_call(['git', 'rebase', '--autostash'])
    subprocess.check_call(['git', 'add', '.'])
    try:
        subprocess.check_call(['git', 'stash', 'pop'])
    except subprocess.CalledProcessError:
        # In case there was nothing to stash.
        # Finish quietly.
        pass

    # Parse `git status --porcelain` to handle some merge conflicts and generate the removed recipe list.
    changed_files = subprocess.check_output(['git', 'status', '--porcelain', recipe_directory_name],
                                            universal_newlines=True)
    changed_files = changed_files.splitlines()

    # Add all files from AU conflicts. They are new files that we weren't tracking previously.
    # Adding them resolves the conflict and doesn't actually add anything to the index.
    new_file_conflicts = filter(lambda _: _.startswith("AU "), changed_files)
    new_file_conflicts = map(lambda _ : _.replace("AU", "", 1).lstrip(), new_file_conflicts)
    for each_new_file in new_file_conflicts:
        subprocess.check_call(['git', 'add', each_new_file])

    # Generate a fresh listing of recipes removed.
    #
    # * Each line we get back is a change to a file in the recipe directory.
    # * We narrow the list down to recipes that are staged for deletion (ignores examples).
    # * Then we clean up the list so that it only has the recipe names.
    removed_recipes = filter(lambda _: _.startswith("D "), changed_files)
    removed_recipes = map(lambda _ : _.replace("D", "", 1).lstrip(), removed_recipes)
    removed_recipes = map(lambda _ : os.path.relpath(_, recipe_directory_name), removed_recipes)
    removed_recipes = map(lambda _ : _.split(os.path.sep)[0], removed_recipes)
    removed_recipes = sorted(set(removed_recipes))

    # Commit any removed packages.
    subprocess.check_call(['git', 'status'])
    if removed_recipes:
        msg = ('Removed recipe{s} ({}) after converting into feedstock{s}.'
               ''.format(', '.join(removed_recipes),
                         s=('s' if len(removed_recipes) > 1 else '')))
        msg += ' [ci skip]' if exit_code == 0 else ' [skip appveyor]'
        if is_merged_pr:
            # Capture the output, as it may contain the GH_TOKEN.
            out = subprocess.check_output(['git', 'remote', 'add', 'upstream_with_token',
                                           'https://conda-forge-manager:{}@github.com/conda-forge/staged-recipes'.format(os.environ['GH_TOKEN'])],
                                          stderr=subprocess.STDOUT)
            subprocess.check_call(['git', 'commit', '-m', msg])
            # Capture the output, as it may contain the GH_TOKEN.
            out = subprocess.check_output(['git', 'push', 'upstream_with_token', os.environ.get('TRAVIS_BRANCH')],
                                          stderr=subprocess.STDOUT)
        else:
            print('Would git commit, with the following message: \n   {}'.format(msg))

    if gh:
        # Get our final rate limit info.
        print_rate_limiting_info(gh)

    sys.exit(exit_code)
<|endoftext|>


<commit_before>class Database(object):
    def _get_value(self):
        return None

    def _get_value_at(self, t):
        return None

    def _get_value_from_to(self, fr, to):
        return []

    def _set_value(self, t, value):
        return False

class Volatile(object):
    volpersist_nb_values = 100
    volpersist_save_lost = True

    def __init__(self):
        super(Volatile, self).__init__()
        self._persisted_volatile_values = []

    def _get_value(self):
        if self._persisted_volatile_values:
            return sorted(self._persisted_volatile_values,
                    key = lambda x: x[0])[-1]
        return super(Volatile, self)._get_value()

    def _get_value_at(self, t):
        if self._persisted_volatile_values:
            v = sorted(self._persisted_volatile_values,
                    key = lambda x: abs(x[0] - t))[0]
            if abs(v[0] - t) <= type(self).update_rate:
                return v
        return super(Volatile, self)._get_value_at(t)

    def _get_value_from_to(self, fr, to):
        return []

    def _set_value(self, t, value):
        self._persisted_volatile_values += [(t, value)]
        if len(self._persisted_volatile_values) \
            > type(self).volpersist_nb_values:
                if type(self).volpersist_save_lost:
                    lost_value = self._persisted_volatile_values[0]
                    super(Volatile, self)._set_value(*lost_value)
                self._persisted_volatile_values = \
                        self._persisted_volatile_values[1:]
        return True

<commit_msg>Ajout du _get_from_to pour la persistance volatile<commit_after>class Database(object):
    def _get_value(self):
        return None

    def _get_value_at(self, t):
        return None

    def _get_value_from_to(self, fr, to):
        return []

    def _set_value(self, t, value):
        return False

class Volatile(object):
    volpersist_nb_values = 100
    volpersist_save_lost = True

    def __init__(self):
        super(Volatile, self).__init__()
        self._persisted_volatile_values = []

    def _get_value(self):
        if self._persisted_volatile_values:
            return sorted(self._persisted_volatile_values,
                    key = lambda x: x[0])[-1]
        return super(Volatile, self)._get_value()

    def _get_value_at(self, t):
        if self._persisted_volatile_values:
            v = sorted(self._persisted_volatile_values,
                    key = lambda x: abs(x[0] - t))[0]
            if abs(v[0] - t) <= type(self).update_rate:
                return v
        return super(Volatile, self)._get_value_at(t)

    def _get_value_from_to(self, fr, to):
        res = filter(lambda x: fr <= x[0] <= to,
                self._persisted_volatile_values)
        if res:
            return res
        return super(Volatile, self)._get_value_from_to(fr, to)

    def _set_value(self, t, value):
        self._persisted_volatile_values += [(t, value)]
        if len(self._persisted_volatile_values) \
            > type(self).volpersist_nb_values:
                if type(self).volpersist_save_lost:
                    lost_value = self._persisted_volatile_values[0]
                    super(Volatile, self)._set_value(*lost_value)
                self._persisted_volatile_values = \
                        self._persisted_volatile_values[1:]
        return True

<|endoftext|>


<commit_before>########################################################################
# Copyright (c) 2014, Brookhaven Science Associates, Brookhaven        #
# National Laboratory. 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 Brookhaven Science Associates, Brookhaven  #
#   National Laboratory 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 HOLDER 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 OTHERWISE) ARISING   #
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE   #
# POSSIBILITY OF SUCH DAMAGE.                                          #
########################################################################
"""
This module is for decorators related to testing.
Much of this code is inspired by the code in matplotlib.  Exact copies
are noted.
"""
from skxray.testing.noseclasses import (KnownFailureTest,
                                       KnownFailureDidNotFailTest)

import nose
from nose.tools import make_decorator

def known_fail_if(cond):
    """
    Make sure a known failure fails.
    This function is a decorator factory.
    """
    # make the decorator function
    def dec(in_func):
        # make the wrapper function
        # if the condition is True
        if cond:
            def inner_wrap():
                # try the test anywoy
                try:
                    in_func()
                # when in fails, raises KnownFailureTest
                # which is registered with nose and it will be marked
                # as K in the results
                except Exception:
                    raise KnownFailureTest()
                # if it does not fail, raise KnownFailureDidNotFailTest which
                # is a normal exception.  This may seem counter-intuitive
                # but knowing when tests that _should_ fail don't can be useful
                else:
                    raise KnownFailureDidNotFailTest()
            # use `make_decorator` from nose to make sure that the meta-data on
            # the function is forwarded properly (name, teardown, setup, etc)
            return make_decorator(in_func)(inner_wrap)

        # if the condition is false, don't make a wrapper function
        # this is effectively a no-op
        else:
            return in_func

    # return the decorator function
    return dec

def skip_if(cond, msg=''):
    """
    A decorator to skip a test if condition is met
    """
    def dec(in_func):
        if cond:
            def wrapper():
                raise nose.SkipTest(msg)
            return make_decorator(in_func)(wrapper)
        else:
            return in_func
    return dec
<commit_msg>TST: known gpu fail<commit_after>########################################################################
# Copyright (c) 2014, Brookhaven Science Associates, Brookhaven        #
# National Laboratory. 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 Brookhaven Science Associates, Brookhaven  #
#   National Laboratory 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 HOLDER 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 OTHERWISE) ARISING   #
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE   #
# POSSIBILITY OF SUCH DAMAGE.                                          #
########################################################################
"""
This module is for decorators related to testing.
Much of this code is inspired by the code in matplotlib.  Exact copies
are noted.
"""
from pyiid.testing.noseclasses import (KnownFailureTest,
                                       KnownFailureDidNotFailTest)

import nose
from nose.tools import make_decorator

def known_fail_if(cond):
    """
    Make sure a known failure fails.
    This function is a decorator factory.
    """
    # make the decorator function
    def dec(in_func):
        # make the wrapper function
        # if the condition is True
        if cond:
            def inner_wrap():
                # try the test anywoy
                try:
                    in_func()
                # when in fails, raises KnownFailureTest
                # which is registered with nose and it will be marked
                # as K in the results
                except Exception:
                    raise KnownFailureTest()
                # if it does not fail, raise KnownFailureDidNotFailTest which
                # is a normal exception.  This may seem counter-intuitive
                # but knowing when tests that _should_ fail don't can be useful
                else:
                    raise KnownFailureDidNotFailTest()
            # use `make_decorator` from nose to make sure that the meta-data on
            # the function is forwarded properly (name, teardown, setup, etc)
            return make_decorator(in_func)(inner_wrap)

        # if the condition is false, don't make a wrapper function
        # this is effectively a no-op
        else:
            return in_func

    # return the decorator function
    return dec

def skip_if(cond, msg=''):
    """
    A decorator to skip a test if condition is met
    """
    def dec(in_func):
        if cond:
            def wrapper():
                raise nose.SkipTest(msg)
            return make_decorator(in_func)(wrapper)
        else:
            return in_func
    return dec
<|endoftext|>