<commit_before>import re
from sets import Set

class EditorConfigToolObject(object):

    """Base class for EditorConfig tools"""

    byte_order_marks = {
        '\xef\xbb\xbf': 'utf-8-bom',
        '\xfe\xff': 'utf-16be',
        '\xff\xfe': 'utf-16le',
        '\x00\x00\xfe\xff': 'utf-32be',
        '\xff\xfe\x00\x00': 'utf-32le',
    }

    line_endings = {
        'crlf': '\r\n',
        'lf': '\n',
        'cr': '\r',
    }
    newlines = dict(zip(line_endings.values(), line_endings.keys()))

class EditorConfigChecker(EditorConfigToolObject):

    """Allows checking file validity based on given EditorConfig"""

    def __init__(self, fix=False):
        self.auto_fix = fix
        self.errors = Set()

    def check_indentation(self, line, indent_style):
        """Return error string iff incorrect characters found in indentation"""
        if indent_style == 'space' and '\t' in line:
            self.errors.add("Tab indentation found")
        elif indent_style == 'tab' and re.search('^\s* \s* ', line):
            self.errors.add("Space indentation found")
        return line

    def check_charset(self, line, charset):
        """Return error string iff incorrect BOM found for expected charset"""
        found_charset = None
        if charset in ('utf-8', 'latin1'):
            charset = None
        for bom, given_charset in self.byte_order_marks.items():
            if line.startswith(bom):
                found_charset = given_charset
        if found_charset != charset:
            if not found_charset:
                found_charset = "utf-8 or latin1"
            self.errors.add("Charset %s found" % found_charset)
        return line

    def check_final_newline(self, line, insert_final_newline):
        """Return given final line with newline added/removed if necessary"""
        has_final_newline = line[-1:] in ('\r', '\n')
        if (insert_final_newline in ('true', 'false') and
            insert_final_newline != str(has_final_newline).lower()):
            if has_final_newline:
                self.errors.add("Final newline found")
            else:
                self.errors.add("No final newline found")
        if self.auto_fix:
            if insert_final_newline == 'true':
                return re.sub(r'\r?\n?$', r'\n', line)
            elif insert_final_newline == 'false':
                return re.sub(r'\r?\n?$', '', line)

    def check_trailing_whitespace(self, line, trim_trailing_whitespace):
        """Return line with whitespace trimmed if necessary"""
        if trim_trailing_whitespace == 'true':
            new_line = re.sub(r'[ \t]*(\r?\n?)$', r'\1', line)
            if new_line != line:
                self.errors.add("Trailing whitespace found")
            if self.auto_fix:
                line = new_line
        return line

    def check(self, filename, properties):

        """Return error string list if file format doesn't match properties"""

        # Error list, current line, correctly indented line count, line number
        lines = []
        line = None
        correctly_indented = 0
        lineno = 0

        def handle_line(function, property_name):
            """Add to error list if current line error for given function"""
            if property_name in properties:
                return function(line, properties[property_name])
            else:
                return line

        with open(filename, 'Ur+' if self.auto_fix else 'Ur') as f:
            # Loop over file lines and append each error found to error list
            if properties.get('end_of_line') in self.line_endings:
                end_of_line = properties['end_of_line']
                newline = self.line_endings[end_of_line]
            else:
                end_of_line = None
                newline = None
            for lineno, line in enumerate(f):
                if end_of_line is None:
                    newline = f.newlines[0]
                    end_of_line = self.newlines[newline]
                if lineno == 0:
                    handle_line(self.check_charset, 'charset')
                line = handle_line(self.check_trailing_whitespace,
                           'trim_trailing_whitespace')
                if ('indent_style' in properties and
                    'indent_size' in properties and
                    properties['indent_size'] == properties['tab_width']):
                    line = handle_line(self.check_indentation, 'indent_style')
                if properties.get('indent_style') == 'space':
                    spaces = len(re.search('^ *', line).group(0))
                    if (spaces <= 1 or
                        spaces % int(properties['indent_size']) == 0):
                        correctly_indented += 1
                else:
                    correctly_indented += 1
                if self.auto_fix:
                    if end_of_line:
                        line = line.replace('\n', newline)
                    lines.append(line)
            if type(f.newlines) is tuple:
                self.errors.add("Mixed line endings found: %s" %
                                ','.join(self.newlines[n] for n in f.newlines))
            elif newline != f.newlines:
                self.errors.add("Incorrect line ending found: %s" %
                                self.newlines[f.newlines])
            if lineno and float(correctly_indented) / lineno < 0.05:
                self.errors.add("Over 5% of lines appear to be incorrectly indented")
            if self.auto_fix and line is not None:
                line = handle_line(self.check_final_newline,
                                   'insert_final_newline')
                line = line.replace('\n', newline)
                lines[-1] = line
            if self.auto_fix:
                f.seek(0)
                f.writelines(lines)
                f.truncate()
        errors = list(self.errors)
        self.errors.clear()
        return errors
<commit_msg>detect space indentation if only one space<commit_after>import re
from sets import Set

class EditorConfigToolObject(object):

    """Base class for EditorConfig tools"""

    byte_order_marks = {
        '\xef\xbb\xbf': 'utf-8-bom',
        '\xfe\xff': 'utf-16be',
        '\xff\xfe': 'utf-16le',
        '\x00\x00\xfe\xff': 'utf-32be',
        '\xff\xfe\x00\x00': 'utf-32le',
    }

    line_endings = {
        'crlf': '\r\n',
        'lf': '\n',
        'cr': '\r',
    }
    newlines = dict(zip(line_endings.values(), line_endings.keys()))

class EditorConfigChecker(EditorConfigToolObject):

    """Allows checking file validity based on given EditorConfig"""

    def __init__(self, fix=False):
        self.auto_fix = fix
        self.errors = Set()

    def check_indentation(self, line, indent_style):
        """Return error string iff incorrect characters found in indentation"""
        if indent_style == 'space' and '\t' in line:
            self.errors.add("Tab indentation found")
        elif indent_style == 'tab' and re.search('^\s* \s*', line):
            self.errors.add("Space indentation found")
        return line

    def check_charset(self, line, charset):
        """Return error string iff incorrect BOM found for expected charset"""
        found_charset = None
        if charset in ('utf-8', 'latin1'):
            charset = None
        for bom, given_charset in self.byte_order_marks.items():
            if line.startswith(bom):
                found_charset = given_charset
        if found_charset != charset:
            if not found_charset:
                found_charset = "utf-8 or latin1"
            self.errors.add("Charset %s found" % found_charset)
        return line

    def check_final_newline(self, line, insert_final_newline):
        """Return given final line with newline added/removed if necessary"""
        has_final_newline = line[-1:] in ('\r', '\n')
        if (insert_final_newline in ('true', 'false') and
            insert_final_newline != str(has_final_newline).lower()):
            if has_final_newline:
                self.errors.add("Final newline found")
            else:
                self.errors.add("No final newline found")
        if self.auto_fix:
            if insert_final_newline == 'true':
                return re.sub(r'\r?\n?$', r'\n', line)
            elif insert_final_newline == 'false':
                return re.sub(r'\r?\n?$', '', line)

    def check_trailing_whitespace(self, line, trim_trailing_whitespace):
        """Return line with whitespace trimmed if necessary"""
        if trim_trailing_whitespace == 'true':
            new_line = re.sub(r'[ \t]*(\r?\n?)$', r'\1', line)
            if new_line != line:
                self.errors.add("Trailing whitespace found")
            if self.auto_fix:
                line = new_line
        return line

    def check(self, filename, properties):

        """Return error string list if file format doesn't match properties"""

        # Error list, current line, correctly indented line count, line number
        lines = []
        line = None
        correctly_indented = 0
        lineno = 0

        def handle_line(function, property_name):
            """Add to error list if current line error for given function"""
            if property_name in properties:
                return function(line, properties[property_name])
            else:
                return line

        with open(filename, 'Ur+' if self.auto_fix else 'Ur') as f:
            # Loop over file lines and append each error found to error list
            if properties.get('end_of_line') in self.line_endings:
                end_of_line = properties['end_of_line']
                newline = self.line_endings[end_of_line]
            else:
                end_of_line = None
                newline = None
            for lineno, line in enumerate(f):
                if end_of_line is None:
                    newline = f.newlines[0]
                    end_of_line = self.newlines[newline]
                if lineno == 0:
                    handle_line(self.check_charset, 'charset')
                line = handle_line(self.check_trailing_whitespace,
                           'trim_trailing_whitespace')
                if ('indent_style' in properties and
                    'indent_size' in properties and
                    properties['indent_size'] == properties['tab_width']):
                    line = handle_line(self.check_indentation, 'indent_style')
                if properties.get('indent_style') == 'space':
                    spaces = len(re.search('^ *', line).group(0))
                    if (spaces <= 1 or
                        spaces % int(properties['indent_size']) == 0):
                        correctly_indented += 1
                else:
                    correctly_indented += 1
                if self.auto_fix:
                    if end_of_line:
                        line = line.replace('\n', newline)
                    lines.append(line)
            if type(f.newlines) is tuple:
                self.errors.add("Mixed line endings found: %s" %
                                ','.join(self.newlines[n] for n in f.newlines))
            elif newline != f.newlines:
                self.errors.add("Incorrect line ending found: %s" %
                                self.newlines[f.newlines])
            if lineno and float(correctly_indented) / lineno < 0.05:
                self.errors.add("Over 5% of lines appear to be incorrectly indented")
            if self.auto_fix and line is not None:
                line = handle_line(self.check_final_newline,
                                   'insert_final_newline')
                line = line.replace('\n', newline)
                lines[-1] = line
            if self.auto_fix:
                f.seek(0)
                f.writelines(lines)
                f.truncate()
        errors = list(self.errors)
        self.errors.clear()
        return errors
<|endoftext|>


<commit_before>#/usr/bin/env python

# Requirements:
# NumPy
# Cantera
# PuLP

import numpy
import Cantera
import pulp

# Optional:
# Installed LP solvers (Gurobi, CPLEX, CBC, COIN)

def reaction_elim(states, ideal_gas, atol, rtol):
    """
    Purpose: Carries out reaction elimination (Bhattacharjee, et al.,
    Comb Flame, 2003) on the mechanism specified in ideal_gas at the
    conditions specified in states, using the absolute tolerances
    specified in atol, and relative tolerances specified in rtol.

    Arguments:
    states (list of list of floats, or 2-D numpy.ndarray of floats):
    each element of the outer list (or each row of the 2-D
    numpy.ndarray) corresponds to a system state (or condition).
    Conditions consist of temperature and species mass fractions
    (in the order that they are specified in the Cantera mechanism).
    Temperature must be the first element in the system state list
    (or 1-D numpy.ndarray); subsequent elements must be species mass
    fractions, in the order that they are specified in the Cantera
    mechanism.
    ideal_gas (Cantera.Solution): Cantera.Solution object specifying
    a chemical reaction mechanism and the thermodynamic properties of
    its constituent species
    atol (list of floats or 1-D numpy.ndarray of floats): list of
    absolute tolerances; len(atol) == states.shape[1] ==
    ideal_gas.nSpecies() + 1
    rtol (list of floats or 1-D numpy.ndarray of floats): list of
    relative tolerances; len(rtol) == states.shape[1] ==
    ideal_gas.nSpecies() + 1

    Returns:
    z (list of ints, or 1-D numpy.ndarray of ints): binary variables
    indicating which reactions should be kept, and which should be
    eliminated
    status (str): indicates the LP solver status; is one of "Not
    Solved", "Infeasible", "Unbounded", "Undefined", "Optimal"

    Warnings:
    This function alters the state of ideal_gas. If the state of that
    object prior to calling this function needs to be preserved,
    copy the object.

    """

    # Set up the lists needed for indexing
    rxn_list = range(0, ideal_gas.nReactions())
    rxn_strings = [str(n) for n in rxn_list]

    # Instantiate binary variables for integer linear program
    z_var = pulp.LpVariable.dicts('rxn_', rxn_strings, 0, 1, 'Integer')

    # Instantiate integer linear program and objective function
    rxn_elim_ILP = pulp.LpProblem("Reaction Elimination", pulp.LpMinimize)
    rxn_elim_ILP += pulp.lpSum([z_var[s] for s
                                in rxn_strings]), "Number of reactions"

    # Calculate condition-independent data and store
    molar_mass = ideal_gas.molarMasses()
    stoich_matrix = ideal_gas.productStoichCoeffs() - \
        ideal_gas.reactantStoichCoeffs()
    mass_stoich_prod = numpy.dot(numpy.diag(molar_mass), stoich_matrix)
    ideal_gas.setPressure(Cantera.OneAtm)

    # Add constraints: loop over data points
    for k in range(0, len(states)):

        # Calculate condition-dependent data
        ideal_gas.setTemperature(states[k][0])
        ideal_gas.setMassFractions(states[k][1:])
        rxn_rate = ideal_gas.netRatesOfProgress()
        cp_mass = ideal_gas.cp_mass()
        enthalpy_mass = ideal_gas.enthalpies_RT() * \
            ideal_gas.temperature() * Cantera.GasConstant
        rho = ideal_gas.density()

        # Add two temperature error constraints (lower, upper bounds)
        coeffs_temp = numpy.dot(enthalpy_mass, mass_stoich_prod) / \
            (rho * cp_mass)
        temp_dot = numpy.dot(coeffs_temp, rxn_rate)
        rxn_elim_ILP += pulp.lpSum([coeffs_temp[i] * rxn_rate[i] *
            (1 - z_var[rxn_strings[i]]) for i in rxn_list]) >= \
            -atol[0] - rtol[0] * abs(temp_dot), \
            "Temperature Error Lower Bound for Data Point " + str(k+1)
        rxn_elim_ILP += pulp.lpSum([coeffs_temp[i] * rxn_rate[i] *
            (1 - z_var[rxn_strings[i]]) for i in rxn_list]) <= \
            atol[0] + rtol[0] * abs(temp_dot), \
            "Temperature Error Upper Bound for Data Point " + str(k+1)

        ydot = numpy.dot(mass_stoich_prod, rxn_rate) / rho
        # Add constraints: Loop over species mass fractions
        for j in range(0, ideal_gas.nSpecies()):

            # Add two species mass fraction error constraints (lower, upper
            # bounds)
            rxn_elim_ILP += pulp.lpSum([mass_stoich_prod[j, i] *
                (1 - z_var[rxn_strings[i]]) * rxn_rate[i] / rho
                for i in rxn_list]) >= -atol[j + 1] - rtol[j + 1] * \
                abs(ydot[j]), "Mass Fraction Species " + str(j+1) + \
                " Error Lower Bound for Data Point " + str(k+1)
            rxn_elim_ILP += pulp.lpSum([mass_stoich_prod[j, i] *
                (1 - z_var[rxn_strings[i]]) * rxn_rate[i] / rho
                for i in rxn_list]) <= atol[j + 1] + rtol[j + 1] * \
                abs(ydot[j]), "Mass Fraction Species " + str(j+1) + \
                " Error Upper Bound for Data Point " + str(k+1)

    # Solve integer linear program
    rxn_elim_ILP.solve()

    # Return list of binary variables, solver status
    z = [int(v.value()) for v in rxn_elim_ILP.variables()]
    return z, pulp.LpStatus[rxn_elim_ILP.status]
<commit_msg>Refactored ChemReduce.<commit_after>#/usr/bin/env python

# Requirements:
# NumPy
# Cantera
# PuLP

import numpy
import Cantera
import pulp

# Optional:
# Installed LP solvers (Gurobi, CPLEX, CBC, COIN)

def calc_cond_indep_data(ideal_gas):
    """
    Purpose: Calculate the condition-independent data needed for reaction
    elimination: the molar mass-stoichiometric matrix product

    Arguments:
    ideal_gas (Cantera.Solution): Cantera.Solution object specifying
    a chemical reaction mechanism and the thermodynamic properties of
    its constituent species

    Returns:
    mass_stoich_prod (2-D numpy.ndarray of floats): product of diagonal
    matrix of molar masses and stoichiometry matrix
    """

    molar_mass = ideal_gas.molarMasses()
    stoich_matrix = (ideal_gas.productStoichCoeffs() -
        ideal_gas.reactantStoichCoeffs())
    mass_stoich_prod = numpy.dot(numpy.diag(molar_mass), stoich_matrix)

    return mass_stoich_prod

def calc_cond_dep_data(state, ideal_gas):
    """
    Purpose: Calculate the condition-dependent data needed for reaction
    elimination:
    - species mass enthalpies
    - reaction rates
    - mass-based constant pressure heat capacity
    - mass density

    Arguments:
    state (list of floats, or 1-D numpy.ndarray of floats): Reaction
    conditions consisting of temperature and species mass fractions
    (in the order that they are specified in the Cantera mechanism).
    Temperature must be the first element in the system state list
    (or 1-D numpy.ndarray); subsequent elements must be species mass
    fractions, in the order that they are specified in the Cantera
    mechanism.
    ideal_gas (Cantera.Solution): Cantera.Solution object specifying a
    chemical reaction mechanism and the thermodynamic properties of its
    constituent species; uses state of ideal_gas to calculate properties

    Returns:
    rxn_rate (1-D numpy.ndarray of floats): (row) vector of reaction rates
    cp_mass (float): mass-based constant pressure heat capacity
    enthalpy_mass (1-D numpy.ndarray of floats): (row) vector of species
        mass (or specific) enthalpies
    rho (float): mass density

    """

    ideal_gas.setTemperature(state[0])
    ideal_gas.setMassFractions(state[1:])

    rxn_rate = ideal_gas.netRatesOfProgress()
    cp_mass = ideal_gas.cp_mass()
    enthalpy_mass = (ideal_gas.enthalpies_RT() *
        ideal_gas.temperature() * Cantera.GasConstant)
    rho = ideal_gas.density()

    return (rxn_rate, cp_mass, enthalpy_mass, rho)

def error_constraint_data(state, ideal_gas, mass_stoich_prod, atol, rtol):
    """
    Purpose: Calculates all of the coefficients for the error constraints
    in the point-constrained reaction and species elimination integer
    linear programming formulations.

    Arguments:
    state (list of floats, or 1-D numpy.ndarray of floats): Reaction
    conditions consisting of temperature and species mass fractions
    (in the order that they are specified in the Cantera mechanism).
    Temperature must be the first element in the system state list
    (or 1-D numpy.ndarray); subsequent elements must be species mass
    fractions, in the order that they are specified in the Cantera
    mechanism.
    ideal_gas (Cantera.Solution): Cantera.Solution object specifying a
    chemical reaction mechanism and the thermodynamic properties of its
    constituent species; uses state of ideal_gas to calculate properties
    atol (1-D numpy.ndarray of floats): list of absolute tolerances;
    len(atol) == states.shape[1] == ideal_gas.nSpecies() + 1
    rtol (1-D numpy.ndarray of floats): list of relative tolerances;
    len(rtol) == states.shape[1] == ideal_gas.nSpecies() + 1
    mass_stoich_prod (2-D numpy.ndarray of floats): product of diagonal
    matrix of molar masses and stoichiometry matrix

    Returns:
    coeffs_temp (1-D numpy.ndarray of floats): coefficients for constraints
        on error in time derivative of temperature
    coeffs_y (2-D numpy.ndarray of floats): coefficients for constraints on
        on error in time derivatives of species mass fractions
    rhs_temp (float): right-hand side of constraints on error in time
        derivative of temperature
    rhs_y (1-D numpy.ndarray of floats): right-hand side of constraints on
        error in time derivatives of species mass fractions

    Comments:
    Could refactor this function to use the internal state of ideal_gas,
    but the additional state argument was chosen to make the dependency
    much more explicit.

    """

    (rxn_rate,
     cp_mass,
     enthalpy_mass,
     rho) = calc_cond_dep_data(state, ideal_gas)

    coeffs_temp = numpy.dot(enthalpy_mass, numpy.dot(mass_stoich_prod,
        numpy.diag(rxn_rate))) / (rho * cp_mass)
    temp_dot = numpy.dot(coeffs_temp, rxn_rate)
    rhs_temp = atol[0] + rtol[0] * abs(temp_dot)

    ydot = numpy.dot(mass_stoich_prod, rxn_rate) / rho
    coeffs_y = numpy.dot(mass_stoich_prod, numpy.diag(rxn_rate)) / rho
    rhs_y = atol[1:] + numpy.dot(abs(ydot), numpy.diag(rtol[1:]))

    return coeffs_temp, coeffs_y, rhs_temp, rhs_y

def reaction_elim(states, ideal_gas, atol, rtol):
    """
    Purpose: Carries out reaction elimination (Bhattacharjee, et al.,
    Comb Flame, 2003) on the mechanism specified in ideal_gas at the
    conditions specified in states, using the absolute tolerances
    specified in atol, and relative tolerances specified in rtol.

    Arguments:
    states (list of list of floats, or 2-D numpy.ndarray of floats):
    each element of the outer list (or each row of the 2-D
    numpy.ndarray) corresponds to a system state (or condition).
    Conditions consist of temperature and species mass fractions
    (in the order that they are specified in the Cantera mechanism).
    Temperature must be the first element in the system state list
    (or 1-D numpy.ndarray); subsequent elements must be species mass
    fractions, in the order that they are specified in the Cantera
    mechanism.
    ideal_gas (Cantera.Solution): Cantera.Solution object specifying
    a chemical reaction mechanism and the thermodynamic properties of
    its constituent species
    atol (list of floats or 1-D numpy.ndarray of floats): list of
    absolute tolerances; len(atol) == states.shape[1] ==
    ideal_gas.nSpecies() + 1
    rtol (list of floats or 1-D numpy.ndarray of floats): list of
    relative tolerances; len(rtol) == states.shape[1] ==
    ideal_gas.nSpecies() + 1

    Returns:
    z (list of ints, or 1-D numpy.ndarray of ints): binary variables
    indicating which reactions should be kept, and which should be
    eliminated
    status (str): indicates the LP solver status; is one of "Not
    Solved", "Infeasible", "Unbounded", "Undefined", "Optimal"

    Warnings:
    This function alters the state of ideal_gas. If the state of that
    object prior to calling this function needs to be preserved,
    copy the object.

    """

    # Convert lists to numpy.ndarrays because the data structure is useful
    # for the operators.
    atol = numpy.asarray(atol)
    rtol = numpy.asarray(rtol)

    # Set up the lists needed for indexing
    rxn_list = range(0, ideal_gas.nReactions())
    rxn_strings = [str(n) for n in rxn_list]

    # Instantiate binary variables for integer linear program
    z_var = pulp.LpVariable.dicts('rxn_', rxn_strings, 0, 1, 'Integer')

    # Instantiate integer linear program and objective function
    rxn_elim_ILP = pulp.LpProblem("Reaction Elimination", pulp.LpMinimize)
    rxn_elim_ILP += pulp.lpSum([z_var[s] for s
                                in rxn_strings]), "Number of reactions"

    # Calculate condition-independent data and store
    mass_stoich_prod = calc_cond_indep_data(ideal_gas)
    ideal_gas.setPressure(Cantera.OneAtm)

    # Add constraints: loop over data points
    for k in range(0, len(states)):

        # Calculate condition-dependent data
        (coeffs_temp, coeffs_y, rhs_temp,
        rhs_y) = error_constraint_data(states[k],
            ideal_gas, mass_stoich_prod, atol, rtol)

        # Add two temperature error constraints (lower, upper bounds)
        rxn_elim_ILP += pulp.lpSum([coeffs_temp[i] *
            (1 - z_var[rxn_strings[i]]) for i in rxn_list]) >= -rhs_t, \
            "Temperature Error Lower Bound for Data Point " + str(k+1)
        rxn_elim_ILP += pulp.lpSum([coeffs_temp[i] *
            (1 - z_var[rxn_strings[i]]) for i in rxn_list]) <= rhs_t, \
            "Temperature Error Upper Bound for Data Point " + str(k+1)

        # Add constraints: Loop over species mass fractions
        for j in range(0, ideal_gas.nSpecies()):

            # Add two species mass fraction error constraints (lower, upper
            # bounds)
            rxn_elim_ILP += pulp.lpSum([coeffs_y[j, i] *
                (1 - z_var[rxn_strings[i]]) for i in rxn_list]) >= -rhs_y[j], \
                "Mass Fraction Species " + str(j+1) + \
                " Error Lower Bound for Data Point " + str(k+1)
            rxn_elim_ILP += pulp.lpSum([coeffs_y[j, i] *
                (1 - z_var[rxn_strings[i]]) for i in rxn_list]) <= rhs_y[j], \
                "Mass Fraction Species " + str(j+1) + \
                " Error Upper Bound for Data Point " + str(k+1)

    # Solve integer linear program
    rxn_elim_ILP.solve()

    # Return list of binary variables, solver status
    z = [int(v.value()) for v in rxn_elim_ILP.variables()]
    return z, pulp.LpStatus[rxn_elim_ILP.status]
<|endoftext|>


<commit_before>"""
Created on 11 févr. 2014

@author: inso
"""

import logging
import time
import asyncio
from enum import Enum

from ucoinpy.documents.certification import SelfCertification
from ucoinpy.api import bma as bma
from ucoinpy.api.bma import PROTOCOL_VERSION

from ...tools.exceptions import Error, NoPeerAvailable,\
                                        MembershipNotFoundError
from PyQt5.QtCore import QObject, pyqtSignal

class LocalState(Enum):
    """
    The local state describes how the identity exists locally :
    COMPLETED means all its related datas (certifiers, certified...)
    were succefully downloaded
    PARTIAL means not all data are present locally
    NOT_FOUND means it could not be found anywhere
    """
    NOT_FOUND = 0
    PARTIAL = 1
    COMPLETED = 2

class BlockchainState(Enum):
    """
    The blockchain state describes how the identity
    was found :
    VALIDATED means it was found in the blockchain
    BUFFERED means it was found via a lookup but not in the
    blockchain
    NOT_FOUND means it could not be found anywhere
    """
    NOT_FOUND = 0
    BUFFERED = 1
    VALIDATED = 2

class Identity(QObject):
    """
    A person with a uid and a pubkey
    """
    def __init__(self, uid, pubkey, local_state, blockchain_state):
        """
        Initializing a person object.

        :param str uid: The person uid, also known as its uid on the network
        :param str pubkey: The person pubkey
        :param LocalState local_state: The local status of the identity
        :param BlockchainState blockchain_state: The blockchain status of the identity
        """
        super().__init__()
        self.uid = uid
        self.pubkey = pubkey
        self.local_state = local_state
        self.blockchain_state = blockchain_state

    @classmethod
    def empty(cls, pubkey):
        return cls("", pubkey, LocalState.NOT_FOUND, BlockchainState.NOT_FOUND)

    @classmethod
    def from_handled_data(cls, uid, pubkey, blockchain_state):
        return cls(uid, pubkey, LocalState.COMPLETED, blockchain_state)

    @classmethod
    def from_json(cls, json_data):
        """
        Create a person from json data

        :param dict json_data: The person as a dict in json format
        :return: A new person if pubkey wasn't known, else a new person instance.
        """
        pubkey = json_data['pubkey']
        uid = json_data['uid']
        local_state = LocalState[json_data['local_state']]
        blockchain_state = BlockchainState[json_data['blockchain_state']]

        return cls(uid, pubkey, local_state, blockchain_state)

    @asyncio.coroutine
    def selfcert(self, community):
        """
        Get the identity self certification.
        This request is not cached in the person object.

        :param cutecoin.core.community.Community community: The community target to request the self certification
        :return: A SelfCertification ucoinpy object
        :rtype: ucoinpy.documents.certification.SelfCertification
        """
        timestamp = 0
        lookup_data = yield from community.bma_access.future_request(bma.wot.Lookup, req_args={'search': self.pubkey})
        for result in lookup_data['results']:
            if result["pubkey"] == self.pubkey:
                uids = result['uids']
                for uid_data in uids:
                    if uid_data["meta"]["timestamp"] > timestamp:
                        timestamp = uid_data["meta"]["timestamp"]
                        uid = uid_data["uid"]
                        signature = uid_data["self"]

                return SelfCertification(PROTOCOL_VERSION,
                                         community.currency,
                                         self.pubkey,
                                         timestamp,
                                         uid,
                                         signature)
        return None

    @asyncio.coroutine
    def get_join_date(self, community):
        """
        Get the person join date.
        This request is not cached in the person object.

        :param cutecoin.core.community.Community community: The community target to request the join date
        :return: A datetime object
        """
        try:
            search = yield from community.bma_access.future_request(bma.blockchain.Membership,
                                                                    {'search': self.pubkey})
            if len(search['memberships']) > 0:
                membership_data = search['memberships'][0]
                block = yield from community.bma_access.future_request(bma.blockchain.Block,
                                req_args={'number': membership_data['blockNumber']})
                return block['medianTime']
        except ValueError as e:
            if '404' in str(e) or '400' in str(e):
                raise MembershipNotFoundError(self.pubkey, community.name)
        except NoPeerAvailable as e:
            logging.debug(str(e))
            raise MembershipNotFoundError(self.pubkey, community.name)

    @asyncio.coroutine
    def get_expiration_date(self, community):
        try:
            membership = yield from self.membership(community)
            join_block_number = membership['blockNumber']
            try:
                join_block = yield from community.bma_access.future_request(bma.blockchain.Block,
                                req_args={'number': join_block_number})

                parameters = yield from community.bma_access.future_request(bma.blockchain.Parameters)
                join_date = join_block['medianTime']
                expiration_date = join_date + parameters['sigValidity']
            except NoPeerAvailable:
                expiration_date = None
            except ValueError as e:
                logging.debug("Expiration date not found")
                expiration_date = None
        except MembershipNotFoundError:
            expiration_date = None
        return expiration_date

#TODO: Manage 'OUT' memberships ? Maybe ?
    @asyncio.coroutine
    def membership(self, community):
        """
        Get the person last membership document.

        :param cutecoin.core.community.Community community: The community target to request the join date
        :return: The membership data in BMA json format
        """
        try:
            search = yield from community.bma_access.future_request(bma.blockchain.Membership,
                                           {'search': self.pubkey})
            block_number = -1
            for ms in search['memberships']:
                if ms['blockNumber'] > block_number:
                    block_number = ms['blockNumber']
                    if 'type' in ms:
                        if ms['type'] is 'IN':
                            membership_data = ms
                    else:
                        membership_data = ms
            return membership_data
        except ValueError as e:
            if '404' in str(e)  or '400' in str(e):
                raise MembershipNotFoundError(self.pubkey, community.name)
        except NoPeerAvailable as e:
            logging.debug(str(e))
            raise MembershipNotFoundError(self.pubkey, community.name)

    @asyncio.coroutine
    def published_uid(self, community):
        try:
            data = yield from community.bma_access.future_request(bma.wot.Lookup,
                                 req_args={'search': self.pubkey})
        except ValueError as e:
            if '404' in str(e):
                timestamp = 0

                for result in data['results']:
                    if result["pubkey"] == self.pubkey:
                        uids = result['uids']
                        person_uid = ""
                        for uid_data in uids:
                            if uid_data["meta"]["timestamp"] > timestamp:
                                timestamp = uid_data["meta"]["timestamp"]
                                person_uid = uid_data["uid"]
                            if person_uid == self.uid:
                                return True
        except NoPeerAvailable as e:
            logging.debug(str(e))
        return False

    @asyncio.coroutine
    def is_member(self, community):
        """
        Check if the person is a member of a community

        :param cutecoin.core.community.Community community: The community target to request the join date
        :return: True if the person is a member of a community
        """
        try:
            certifiers = yield from community.bma_access.future_request(bma.wot.CertifiersOf,
                                                                        {'search': self.pubkey})
            return certifiers['isMember']
        except ValueError as e:
            if '404' in str(e) or '400' in str(e):
                pass
            else:
                raise
        except NoPeerAvailable as e:
            logging.debug(str(e))
        return False

    @asyncio.coroutine
    def certifiers_of(self, identities_registry, community):
        """
        Get the list of this person certifiers

        :param cutecoin.core.registry.identities.IdentitiesRegistry identities_registry: The identities registry
        :param cutecoin.core.community.Community community: The community target to request the join date
        :return: The list of the certifiers of this community
        :rtype: list
        """
        certifiers = list()
        try:
            data = yield from community.bma_access.future_request(bma.wot.CertifiersOf,
                                                                  {'search': self.pubkey})

            for certifier_data in data['certifications']:
                certifier = {}
                certifier['identity'] = identities_registry.from_handled_data(certifier_data['uid'],
                                                                              certifier_data['pubkey'],
                                                                              BlockchainState.VALIDATED,
                                                                              community)
                certifier['cert_time'] = certifier_data['cert_time']['medianTime']
                certifier['block_number'] = certifier_data['cert_time']['block']
                certifiers.append(certifier)
        except ValueError as e:
            if '404' in str(e):
                logging.debug('bma.wot.CertifiersOf request error: {0}'.format(str(e)))
        except NoPeerAvailable as e:
            logging.debug(str(e))

        try:
            data = yield from community.bma_access.future_request(bma.wot.Lookup, {'search': self.pubkey})
            for result in data['results']:
                if result["pubkey"] == self.pubkey:
                    self._refresh_uid(result['uids'])
                    for uid_data in result['uids']:
                        for certifier_data in uid_data['others']:
                            for uid in certifier_data['uids']:
                                # add a certifier
                                certifier = {}
                                certifier['identity'] = identities_registry.\
                                    from_handled_data(uid,
                                                      certifier_data['pubkey'],
                                                      BlockchainState.BUFFERED,
                                                      community)
                                if certifier['identity'] not in certifiers:
                                    block = yield from community.bma_access.future_request(bma.blockchain.Block,
                                                                         {'number': certifier_data['meta']['block_number']})
                                    certifier['cert_time'] = block['medianTime']
                                    certifier['block_number'] = None

                                    certifiers.append(certifier)
        except ValueError as e:
            logging.debug("Lookup error : {0}".format(str(e)))
        except NoPeerAvailable as e:
            logging.debug(str(e))
        return certifiers

    @asyncio.coroutine
    def unique_valid_certifiers_of(self, identities_registry, community):
        certifier_list = yield from self.certifiers_of(identities_registry, community)
        unique_valid = []
        #  add certifiers of uid
        for certifier in tuple(certifier_list):
            # add only valid certification...
            cert_expired = yield from community.certification_expired(certifier['cert_time'])
            if cert_expired:
                continue

            # keep only the latest certification
            already_found = [c['identity'].pubkey for c in unique_valid]
            if certifier['identity'].pubkey in already_found:
                index = already_found.index(certifier['identity'].pubkey)
                if certifier['cert_time'] > unique_valid[index]['cert_time']:
                    unique_valid[index] = certifier
            else:
                unique_valid.append(certifier)
        return unique_valid

    @asyncio.coroutine
    def certified_by(self, identities_registry, community):
        """
        Get the list of persons certified by this person
        :param cutecoin.core.registry.IdentitiesRegistry identities_registry: The registry
        :param cutecoin.core.Community community: The community

        :param cutecoin.core.community.Community community: The community target to request the join date
        :return: The list of the certified persons of this community in BMA json format
        :rtype: list
        """
        certified_list = list()
        try:
            data = yield from community.bma_access.future_request(bma.wot.CertifiedBy, {'search': self.pubkey})
            for certified_data in data['certifications']:
                certified = {}
                certified['identity'] = identities_registry.from_handled_data(certified_data['uid'],
                                                                              certified_data['pubkey'],
                                                                              BlockchainState.VALIDATED,
                                                                              community)
                certified['cert_time'] = certified_data['cert_time']['medianTime']
                certified['block_number'] = certified_data['cert_time']['block']
                certified_list.append(certified)
        except ValueError as e:
            if '404' in str(e):
                logging.debug('bma.wot.CertifiersOf request error')
        except NoPeerAvailable as e:
            logging.debug(str(e))

        try:
            data = yield from community.bma_access.future_request(bma.wot.Lookup, {'search': self.pubkey})
            for result in data['results']:
                if result["pubkey"] == self.pubkey:
                    self._refresh_uid(result['uids'])
                    for certified_data in result['signed']:
                        certified = {}
                        certified['identity'] = identities_registry.from_handled_data(certified_data['uid'],
                                                                          certified_data['pubkey'],
                                                                          BlockchainState.BUFFERED,
                                                                          community)
                        if certified['identity'] not in certified_list:
                            certified['cert_time'] = certified_data['meta']['timestamp']
                            certified['block_number'] = None
                            certified_list.append(certified)
        except ValueError as e:
            if '404' in str(e):
                logging.debug('bma.wot.Lookup request error')
        except NoPeerAvailable as e:
            logging.debug(str(e))
        return certified_list

    @asyncio.coroutine
    def unique_valid_certified_by(self, identities_registry, community):
        certified_list = yield from self.certified_by(identities_registry, community)
        unique_valid = []
        #  add certifiers of uid
        for certified in tuple(certified_list):
            # add only valid certification...
            cert_expired = yield from community.certification_expired(certified['cert_time'])
            if cert_expired:
                continue

            # keep only the latest certification
            already_found = [c['identity'].pubkey for c in unique_valid]
            if certified['identity'].pubkey in already_found:
                index = already_found.index(certified['identity'].pubkey)
                if certified['cert_time'] > unique_valid[index]['cert_time']:
                    unique_valid[index] = certified
            else:
                unique_valid.append(certified)
        return unique_valid

    @asyncio.coroutine
    def membership_expiration_time(self, community):
        membership = yield from self.membership(community)
        join_block = membership['blockNumber']
        block = yield from community.get_block(join_block)
        join_date = block['medianTime']
        parameters = yield from community.parameters()
        expiration_date = join_date + parameters['sigValidity']
        current_time = time.time()
        return expiration_date - current_time

    def _refresh_uid(self, uids):
        """
        Refresh UID from uids list, got from a successful lookup request
        :param list uids: UIDs got from a lookup request
        """
        if self.local_state == LocalState.NOT_FOUND:
            for uid_data in uids:
                if uid_data["meta"]["timestamp"] > timestamp:
                    timestamp = uid_data["meta"]["timestamp"]
                    identity_uid = uid_data["uid"]
                    self.uid = identity_uid
                    self.blockchain_state = BlockchainState.BUFFERED
                    self.local_state = LocalState.PARTIAL

    def jsonify(self):
        """
        Get the community as dict in json format.
        :return: The community as a dict in json format
        """
        data = {'uid': self.uid,
                'pubkey': self.pubkey,
                'local_state': self.local_state.name,
                'blockchain_state': self.blockchain_state.name}
        return data

    def __str__(self):
        return "{0} - {1} - {2} - {3}".format(self.uid,
                                        self.pubkey,
                                        self.local_state,
                                        self.blockchain_state)
<commit_msg>Fix syntax error<commit_after>"""
Created on 11 févr. 2014

@author: inso
"""

import logging
import time
import asyncio
from enum import Enum

from ucoinpy.documents.certification import SelfCertification
from ucoinpy.api import bma as bma
from ucoinpy.api.bma import PROTOCOL_VERSION

from ...tools.exceptions import Error, NoPeerAvailable,\
                                        MembershipNotFoundError
from PyQt5.QtCore import QObject, pyqtSignal

class LocalState(Enum):
    """
    The local state describes how the identity exists locally :
    COMPLETED means all its related datas (certifiers, certified...)
    were succefully downloaded
    PARTIAL means not all data are present locally
    NOT_FOUND means it could not be found anywhere
    """
    NOT_FOUND = 0
    PARTIAL = 1
    COMPLETED = 2

class BlockchainState(Enum):
    """
    The blockchain state describes how the identity
    was found :
    VALIDATED means it was found in the blockchain
    BUFFERED means it was found via a lookup but not in the
    blockchain
    NOT_FOUND means it could not be found anywhere
    """
    NOT_FOUND = 0
    BUFFERED = 1
    VALIDATED = 2

class Identity(QObject):
    """
    A person with a uid and a pubkey
    """
    def __init__(self, uid, pubkey, local_state, blockchain_state):
        """
        Initializing a person object.

        :param str uid: The person uid, also known as its uid on the network
        :param str pubkey: The person pubkey
        :param LocalState local_state: The local status of the identity
        :param BlockchainState blockchain_state: The blockchain status of the identity
        """
        super().__init__()
        self.uid = uid
        self.pubkey = pubkey
        self.local_state = local_state
        self.blockchain_state = blockchain_state

    @classmethod
    def empty(cls, pubkey):
        return cls("", pubkey, LocalState.NOT_FOUND, BlockchainState.NOT_FOUND)

    @classmethod
    def from_handled_data(cls, uid, pubkey, blockchain_state):
        return cls(uid, pubkey, LocalState.COMPLETED, blockchain_state)

    @classmethod
    def from_json(cls, json_data):
        """
        Create a person from json data

        :param dict json_data: The person as a dict in json format
        :return: A new person if pubkey wasn't known, else a new person instance.
        """
        pubkey = json_data['pubkey']
        uid = json_data['uid']
        local_state = LocalState[json_data['local_state']]
        blockchain_state = BlockchainState[json_data['blockchain_state']]

        return cls(uid, pubkey, local_state, blockchain_state)

    @asyncio.coroutine
    def selfcert(self, community):
        """
        Get the identity self certification.
        This request is not cached in the person object.

        :param cutecoin.core.community.Community community: The community target to request the self certification
        :return: A SelfCertification ucoinpy object
        :rtype: ucoinpy.documents.certification.SelfCertification
        """
        timestamp = 0
        lookup_data = yield from community.bma_access.future_request(bma.wot.Lookup, req_args={'search': self.pubkey})
        for result in lookup_data['results']:
            if result["pubkey"] == self.pubkey:
                uids = result['uids']
                for uid_data in uids:
                    if uid_data["meta"]["timestamp"] > timestamp:
                        timestamp = uid_data["meta"]["timestamp"]
                        uid = uid_data["uid"]
                        signature = uid_data["self"]

                return SelfCertification(PROTOCOL_VERSION,
                                         community.currency,
                                         self.pubkey,
                                         timestamp,
                                         uid,
                                         signature)
        return None

    @asyncio.coroutine
    def get_join_date(self, community):
        """
        Get the person join date.
        This request is not cached in the person object.

        :param cutecoin.core.community.Community community: The community target to request the join date
        :return: A datetime object
        """
        try:
            search = yield from community.bma_access.future_request(bma.blockchain.Membership,
                                                                    {'search': self.pubkey})
            if len(search['memberships']) > 0:
                membership_data = search['memberships'][0]
                block = yield from community.bma_access.future_request(bma.blockchain.Block,
                                req_args={'number': membership_data['blockNumber']})
                return block['medianTime']
        except ValueError as e:
            if '404' in str(e) or '400' in str(e):
                raise MembershipNotFoundError(self.pubkey, community.name)
        except NoPeerAvailable as e:
            logging.debug(str(e))
            raise MembershipNotFoundError(self.pubkey, community.name)

    @asyncio.coroutine
    def get_expiration_date(self, community):
        try:
            membership = yield from self.membership(community)
            join_block_number = membership['blockNumber']
            try:
                join_block = yield from community.bma_access.future_request(bma.blockchain.Block,
                                req_args={'number': join_block_number})

                parameters = yield from community.bma_access.future_request(bma.blockchain.Parameters)
                join_date = join_block['medianTime']
                expiration_date = join_date + parameters['sigValidity']
            except NoPeerAvailable:
                expiration_date = None
            except ValueError as e:
                logging.debug("Expiration date not found")
                expiration_date = None
        except MembershipNotFoundError:
            expiration_date = None
        return expiration_date

#TODO: Manage 'OUT' memberships ? Maybe ?
    @asyncio.coroutine
    def membership(self, community):
        """
        Get the person last membership document.

        :param cutecoin.core.community.Community community: The community target to request the join date
        :return: The membership data in BMA json format
        """
        try:
            search = yield from community.bma_access.future_request(bma.blockchain.Membership,
                                           {'search': self.pubkey})
            block_number = -1
            for ms in search['memberships']:
                if ms['blockNumber'] > block_number:
                    block_number = ms['blockNumber']
                    if 'type' in ms:
                        if ms['type'] is 'IN':
                            membership_data = ms
                    else:
                        membership_data = ms
            return membership_data
        except ValueError as e:
            if '404' in str(e)  or '400' in str(e):
                raise MembershipNotFoundError(self.pubkey, community.name)
        except NoPeerAvailable as e:
            logging.debug(str(e))
            raise MembershipNotFoundError(self.pubkey, community.name)

    @asyncio.coroutine
    def published_uid(self, community):
        try:
            data = yield from community.bma_access.future_request(bma.wot.Lookup,
                                 req_args={'search': self.pubkey})
        except ValueError as e:
            if '404' in str(e):
                timestamp = 0

                for result in data['results']:
                    if result["pubkey"] == self.pubkey:
                        uids = result['uids']
                        person_uid = ""
                        for uid_data in uids:
                            if uid_data["meta"]["timestamp"] > timestamp:
                                timestamp = uid_data["meta"]["timestamp"]
                                person_uid = uid_data["uid"]
                            if person_uid == self.uid:
                                return True
        except NoPeerAvailable as e:
            logging.debug(str(e))
        return False

    @asyncio.coroutine
    def is_member(self, community):
        """
        Check if the person is a member of a community

        :param cutecoin.core.community.Community community: The community target to request the join date
        :return: True if the person is a member of a community
        """
        try:
            certifiers = yield from community.bma_access.future_request(bma.wot.CertifiersOf,
                                                                        {'search': self.pubkey})
            return certifiers['isMember']
        except ValueError as e:
            if '404' in str(e) or '400' in str(e):
                pass
            else:
                raise
        except NoPeerAvailable as e:
            logging.debug(str(e))
        return False

    @asyncio.coroutine
    def certifiers_of(self, identities_registry, community):
        """
        Get the list of this person certifiers

        :param cutecoin.core.registry.identities.IdentitiesRegistry identities_registry: The identities registry
        :param cutecoin.core.community.Community community: The community target to request the join date
        :return: The list of the certifiers of this community
        :rtype: list
        """
        certifiers = list()
        try:
            data = yield from community.bma_access.future_request(bma.wot.CertifiersOf,
                                                                  {'search': self.pubkey})

            for certifier_data in data['certifications']:
                certifier = {}
                certifier['identity'] = identities_registry.from_handled_data(certifier_data['uid'],
                                                                              certifier_data['pubkey'],
                                                                              BlockchainState.VALIDATED,
                                                                              community)
                certifier['cert_time'] = certifier_data['cert_time']['medianTime']
                certifier['block_number'] = certifier_data['cert_time']['block']
                certifiers.append(certifier)
        except ValueError as e:
            if '404' in str(e):
                logging.debug('bma.wot.CertifiersOf request error: {0}'.format(str(e)))
        except NoPeerAvailable as e:
            logging.debug(str(e))

        try:
            data = yield from community.bma_access.future_request(bma.wot.Lookup, {'search': self.pubkey})
            for result in data['results']:
                if result["pubkey"] == self.pubkey:
                    self._refresh_uid(result['uids'])
                    for uid_data in result['uids']:
                        for certifier_data in uid_data['others']:
                            for uid in certifier_data['uids']:
                                # add a certifier
                                certifier = {}
                                certifier['identity'] = identities_registry.\
                                    from_handled_data(uid,
                                                      certifier_data['pubkey'],
                                                      BlockchainState.BUFFERED,
                                                      community)
                                if certifier['identity'] not in certifiers:
                                    block = yield from community.bma_access.future_request(bma.blockchain.Block,
                                                                         {'number': certifier_data['meta']['block_number']})
                                    certifier['cert_time'] = block['medianTime']
                                    certifier['block_number'] = None

                                    certifiers.append(certifier)
        except ValueError as e:
            logging.debug("Lookup error : {0}".format(str(e)))
        except NoPeerAvailable as e:
            logging.debug(str(e))
        return certifiers

    @asyncio.coroutine
    def unique_valid_certifiers_of(self, identities_registry, community):
        certifier_list = yield from self.certifiers_of(identities_registry, community)
        unique_valid = []
        #  add certifiers of uid
        for certifier in tuple(certifier_list):
            # add only valid certification...
            cert_expired = yield from community.certification_expired(certifier['cert_time'])
            if cert_expired:
                continue

            # keep only the latest certification
            already_found = [c['identity'].pubkey for c in unique_valid]
            if certifier['identity'].pubkey in already_found:
                index = already_found.index(certifier['identity'].pubkey)
                if certifier['cert_time'] > unique_valid[index]['cert_time']:
                    unique_valid[index] = certifier
            else:
                unique_valid.append(certifier)
        return unique_valid

    @asyncio.coroutine
    def certified_by(self, identities_registry, community):
        """
        Get the list of persons certified by this person
        :param cutecoin.core.registry.IdentitiesRegistry identities_registry: The registry
        :param cutecoin.core.Community community: The community

        :param cutecoin.core.community.Community community: The community target to request the join date
        :return: The list of the certified persons of this community in BMA json format
        :rtype: list
        """
        certified_list = list()
        try:
            data = yield from community.bma_access.future_request(bma.wot.CertifiedBy, {'search': self.pubkey})
            for certified_data in data['certifications']:
                certified = {}
                certified['identity'] = identities_registry.from_handled_data(certified_data['uid'],
                                                                              certified_data['pubkey'],
                                                                              BlockchainState.VALIDATED,
                                                                              community)
                certified['cert_time'] = certified_data['cert_time']['medianTime']
                certified['block_number'] = certified_data['cert_time']['block']
                certified_list.append(certified)
        except ValueError as e:
            if '404' in str(e):
                logging.debug('bma.wot.CertifiersOf request error')
        except NoPeerAvailable as e:
            logging.debug(str(e))

        try:
            data = yield from community.bma_access.future_request(bma.wot.Lookup, {'search': self.pubkey})
            for result in data['results']:
                if result["pubkey"] == self.pubkey:
                    self._refresh_uid(result['uids'])
                    for certified_data in result['signed']:
                        certified = {}
                        certified['identity'] = identities_registry.from_handled_data(certified_data['uid'],
                                                                          certified_data['pubkey'],
                                                                          BlockchainState.BUFFERED,
                                                                          community)
                        if certified['identity'] not in certified_list:
                            certified['cert_time'] = certified_data['meta']['timestamp']
                            certified['block_number'] = None
                            certified_list.append(certified)
        except ValueError as e:
            if '404' in str(e):
                logging.debug('bma.wot.Lookup request error')
        except NoPeerAvailable as e:
            logging.debug(str(e))
        return certified_list

    @asyncio.coroutine
    def unique_valid_certified_by(self, identities_registry, community):
        certified_list = yield from self.certified_by(identities_registry, community)
        unique_valid = []
        #  add certifiers of uid
        for certified in tuple(certified_list):
            # add only valid certification...
            cert_expired = yield from community.certification_expired(certified['cert_time'])
            if cert_expired:
                continue

            # keep only the latest certification
            already_found = [c['identity'].pubkey for c in unique_valid]
            if certified['identity'].pubkey in already_found:
                index = already_found.index(certified['identity'].pubkey)
                if certified['cert_time'] > unique_valid[index]['cert_time']:
                    unique_valid[index] = certified
            else:
                unique_valid.append(certified)
        return unique_valid

    @asyncio.coroutine
    def membership_expiration_time(self, community):
        membership = yield from self.membership(community)
        join_block = membership['blockNumber']
        block = yield from community.get_block(join_block)
        join_date = block['medianTime']
        parameters = yield from community.parameters()
        expiration_date = join_date + parameters['sigValidity']
        current_time = time.time()
        return expiration_date - current_time

    def _refresh_uid(self, uids):
        """
        Refresh UID from uids list, got from a successful lookup request
        :param list uids: UIDs got from a lookup request
        """
        timestamp = 0
        if self.local_state == LocalState.NOT_FOUND:
            for uid_data in uids:
                if uid_data["meta"]["timestamp"] > timestamp:
                    timestamp = uid_data["meta"]["timestamp"]
                    identity_uid = uid_data["uid"]
                    self.uid = identity_uid
                    self.blockchain_state = BlockchainState.BUFFERED
                    self.local_state = LocalState.PARTIAL

    def jsonify(self):
        """
        Get the community as dict in json format.
        :return: The community as a dict in json format
        """
        data = {'uid': self.uid,
                'pubkey': self.pubkey,
                'local_state': self.local_state.name,
                'blockchain_state': self.blockchain_state.name}
        return data

    def __str__(self):
        return "{0} - {1} - {2} - {3}".format(self.uid,
                                        self.pubkey,
                                        self.local_state,
                                        self.blockchain_state)
<|endoftext|>


<commit_before>import os
import mimetypes

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO

from django.conf import settings
from django.core.files.base import File
from django.core.files.storage import Storage
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
from django.utils.encoding import force_unicode, smart_str

try:
    from boto.s3.connection import S3Connection, SubdomainCallingFormat
    from boto.exception import S3ResponseError
    from boto.s3.key import Key
except ImportError:
    raise ImproperlyConfigured("Could not load Boto's S3 bindings.\n"
                               "See http://code.google.com/p/boto/")

ACCESS_KEY_NAME     = getattr(settings, 'AWS_ACCESS_KEY_ID', None)
SECRET_KEY_NAME     = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None)
HEADERS             = getattr(settings, 'AWS_HEADERS', {})
STORAGE_BUCKET_NAME = getattr(settings, 'AWS_STORAGE_BUCKET_NAME', None)
AUTO_CREATE_BUCKET  = getattr(settings, 'AWS_AUTO_CREATE_BUCKET', False)
DEFAULT_ACL         = getattr(settings, 'AWS_DEFAULT_ACL', 'public-read')
BUCKET_ACL          = getattr(settings, 'AWS_BUCKET_ACL', DEFAULT_ACL)
QUERYSTRING_AUTH    = getattr(settings, 'AWS_QUERYSTRING_AUTH', True)
QUERYSTRING_EXPIRE  = getattr(settings, 'AWS_QUERYSTRING_EXPIRE', 3600)
REDUCED_REDUNDANCY  = getattr(settings, 'AWS_REDUCED_REDUNDANCY', False)
LOCATION            = getattr(settings, 'AWS_LOCATION', '')
CUSTOM_DOMAIN       = getattr(settings, 'AWS_S3_CUSTOM_DOMAIN', None)
CALLING_FORMAT      = getattr(settings, 'AWS_S3_CALLING_FORMAT', SubdomainCallingFormat())
SECURE_URLS         = getattr(settings, 'AWS_S3_SECURE_URLS', True)
FILE_NAME_CHARSET   = getattr(settings, 'AWS_S3_FILE_NAME_CHARSET', 'utf-8')
FILE_OVERWRITE      = getattr(settings, 'AWS_S3_FILE_OVERWRITE', True)
IS_GZIPPED          = getattr(settings, 'AWS_IS_GZIPPED', False)
PRELOAD_METADATA    = getattr(settings, 'AWS_PRELOAD_METADATA', False)
GZIP_CONTENT_TYPES  = getattr(settings, 'GZIP_CONTENT_TYPES', (
    'text/css',
    'application/javascript',
    'application/x-javascript'
))

if IS_GZIPPED:
    from gzip import GzipFile

def safe_join(base, *paths):
    """
    A version of django.utils._os.safe_join for S3 paths.

    Joins one or more path components to the base path component intelligently.
    Returns a normalized version of the final path.

    The final path must be located inside of the base path component (otherwise
    a ValueError is raised).

    Paths outside the base path indicate a possible security sensitive operation.
    """
    from urlparse import urljoin
    base_path = force_unicode(base)
    paths = map(lambda p: force_unicode(p), paths)
    final_path = urljoin(base_path + ("/" if not base_path.endswith("/") else ""), *paths)
    # Ensure final_path starts with base_path and that the next character after
    # the final path is '/' (or nothing, in which case final_path must be
    # equal to base_path).
    base_path_len = len(base_path)
    if not final_path.startswith(base_path) \
       or final_path[base_path_len:base_path_len+1] not in ('', '/'):
        raise ValueError('the joined path is located outside of the base path'
                         ' component')
    return final_path

class S3BotoStorage(Storage):
    """Amazon Simple Storage Service using Boto"""

    def __init__(self, bucket=STORAGE_BUCKET_NAME, access_key=None,
                       secret_key=None, bucket_acl=BUCKET_ACL, acl=DEFAULT_ACL, headers=HEADERS,
                       gzip=IS_GZIPPED, gzip_content_types=GZIP_CONTENT_TYPES,
                       querystring_auth=QUERYSTRING_AUTH, querystring_expire=QUERYSTRING_EXPIRE,
                       reduced_redundancy=REDUCED_REDUNDANCY,
                       custom_domain=CUSTOM_DOMAIN, secure_urls=SECURE_URLS,
                       location=LOCATION, file_name_charset=FILE_NAME_CHARSET,
                       preload_metadata=PRELOAD_METADATA, calling_format=CALLING_FORMAT):
        self.bucket_acl = bucket_acl
        self.bucket_name = bucket
        self.acl = acl
        self.headers = headers
        self.preload_metadata = preload_metadata
        self.gzip = gzip
        self.gzip_content_types = gzip_content_types
        self.querystring_auth = querystring_auth
        self.querystring_expire = querystring_expire
        self.reduced_redundancy = reduced_redundancy
        self.custom_domain = custom_domain
        self.secure_urls = secure_urls
        self.location = location or ''
        self.location = self.location.lstrip('/')
        self.file_name_charset = file_name_charset

        if not access_key and not secret_key:
             access_key, secret_key = self._get_access_keys()

        self.connection = S3Connection(access_key, secret_key, calling_format=calling_format)
        self._entries = {}

    @property
    def bucket(self):
        if not hasattr(self, '_bucket'):
            self._bucket = self._get_or_create_bucket(self.bucket_name)
        return self._bucket

    @property
    def entries(self):
        if self.preload_metadata and not self._entries:
            self._entries = dict((self._decode_name(entry.key), entry)
                                for entry in self.bucket.list())
        return self._entries

    def _get_access_keys(self):
        access_key = ACCESS_KEY_NAME
        secret_key = SECRET_KEY_NAME
        if (access_key or secret_key) and (not access_key or not secret_key):
            access_key = os.environ.get(ACCESS_KEY_NAME)
            secret_key = os.environ.get(SECRET_KEY_NAME)

        if access_key and secret_key:
            # Both were provided, so use them
            return access_key, secret_key

        return None, None

    def _get_or_create_bucket(self, name):
        """Retrieves a bucket if it exists, otherwise creates it."""
        try:
            return self.connection.get_bucket(name, validate=AUTO_CREATE_BUCKET)
        except S3ResponseError, e:
            if AUTO_CREATE_BUCKET:
                bucket = self.connection.create_bucket(name)
                bucket.set_acl(self.bucket_acl)
                return bucket
            raise ImproperlyConfigured, ("Bucket specified by "
            "AWS_STORAGE_BUCKET_NAME does not exist. Buckets can be "
            "automatically created by setting AWS_AUTO_CREATE_BUCKET=True")

    def _clean_name(self, name):
        # Useful for windows' paths
        return os.path.normpath(name).replace('\\', '/')

    def _normalize_name(self, name):
        try:
            return safe_join(self.location, name).lstrip('/')
        except ValueError:
            raise SuspiciousOperation("Attempted access to '%s' denied." % name)

    def _encode_name(self, name):
        return smart_str(name, encoding=self.file_name_charset)

    def _decode_name(self, name):
        return force_unicode(name, encoding=self.file_name_charset)

    def _compress_content(self, content):
        """Gzip a given string."""
        zbuf = StringIO()
        zfile = GzipFile(mode='wb', compresslevel=6, fileobj=zbuf)
        zfile.write(content.read())
        zfile.close()
        content.file = zbuf
        return content

    def _open(self, name, mode='rb'):
        name = self._normalize_name(self._clean_name(name))
        f = S3BotoStorageFile(name, mode, self)
        if not f.key:
            raise IOError('File does not exist: %s' % name)
        return f

    def _save(self, name, content):
        cleaned_name = self._clean_name(name)
        name = self._normalize_name(cleaned_name)
        headers = self.headers.copy()
        content_type = getattr(content,'content_type', mimetypes.guess_type(name)[0] or Key.DefaultContentType)

        if self.gzip and content_type in self.gzip_content_types:
            content = self._compress_content(content)
            headers.update({'Content-Encoding': 'gzip'})

        content.name = cleaned_name
        k = self.bucket.get_key(self._encode_name(name))
        if not k:
            k = self.bucket.new_key(self._encode_name(name))

        k.set_metadata('Content-Type',content_type)
        k.set_contents_from_file(content, headers=headers, policy=self.acl,
                                 reduced_redundancy=self.reduced_redundancy)
        return cleaned_name

    def delete(self, name):
        name = self._normalize_name(self._clean_name(name))
        self.bucket.delete_key(self._encode_name(name))

    def exists(self, name):
        name = self._normalize_name(self._clean_name(name))
        if self.entries:
            return name in self.entries
        k = self.bucket.new_key(self._encode_name(name))
        return k.exists()

    def listdir(self, name):
        name = self._normalize_name(self._clean_name(name))
        dirlist = self.bucket.list(self._encode_name(name))
        files = []
        dirs = set()
        base_parts = name.split("/") if name else []
        for item in dirlist:
            parts = item.name.split("/")
            parts = parts[len(base_parts):]
            if len(parts) == 1:
                # File
                files.append(parts[0])
            elif len(parts) > 1:
                # Directory
                dirs.add(parts[0])
        return list(dirs),files

    def size(self, name):
        name = self._normalize_name(self._clean_name(name))
        if self.entries:
            entry = self.entries.get(name)
            if entry:
                return entry.size
            return 0
        return self.bucket.get_key(self._encode_name(name)).size

    def modified_time(self, name):
        try:
           from dateutil import parser, tz
        except ImportError:
            raise NotImplementedError()
        name = self._normalize_name(self._clean_name(name))
        entry = self.entries.get(name)
        # only call self.bucket.get_key() if the key is not found
        # in the preloaded metadata.
        if entry is None:
            entry = self.bucket.get_key(self._encode_name(name))
        # convert to string to date
        last_modified_date = parser.parse(entry.last_modified)
        # if the date has no timzone, assume UTC
        if last_modified_date.tzinfo == None:
            last_modified_date = last_modified_date.replace(tzinfo=tz.tzutc())
        # convert date to local time w/o timezone
        return last_modified_date.astimezone(tz.tzlocal()).replace(tzinfo=None)

    def url(self, name):
        name = self._normalize_name(self._clean_name(name))
        if self.custom_domain:
            return "%s://%s/%s" % ('https' if self.secure_urls else 'http', self.custom_domain, name)
        else:
            return self.connection.generate_url(self.querystring_expire, method='GET', \
                    bucket=self.bucket.name, key=self._encode_name(name), query_auth=self.querystring_auth, \
                    force_http=not self.secure_urls)

    def get_available_name(self, name):
        """ Overwrite existing file with the same name. """
        if FILE_OVERWRITE:
            name = self._clean_name(name)
            return name
        return super(S3BotoStorage, self).get_available_name(name)

class S3BotoStorageFile(File):
    def __init__(self, name, mode, storage):
        self._storage = storage
        self.name = name[len(self._storage.location):].lstrip('/')
        self._mode = mode
        self.key = storage.bucket.get_key(self._storage._encode_name(name))
        self._is_dirty = False
        self._file = None

    @property
    def size(self):
        return self.key.size

    @property
    def file(self):
        if self._file is None:
            self._file = StringIO()
            if 'r' in self._mode:
                self._is_dirty = False
                self.key.get_contents_to_file(self._file)
                self._file.seek(0)
        return self._file

    def read(self, *args, **kwargs):
        if 'r' not in self._mode:
            raise AttributeError("File was not opened in read mode.")
        return super(S3BotoStorageFile, self).read(*args, **kwargs)

    def write(self, *args, **kwargs):
        if 'w' not in self._mode:
            raise AttributeError("File was opened for read-only access.")
        self._is_dirty = True
        return super(S3BotoStorageFile, self).write(*args, **kwargs)

    def close(self):
        if self._is_dirty:
            self.key.set_contents_from_file(self._file, headers=self._storage.headers, policy=self._storage.acl)
        self.key.close()
<commit_msg>add support for S3 server-side encryption<commit_after>import os
import mimetypes

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO

from django.conf import settings
from django.core.files.base import File
from django.core.files.storage import Storage
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
from django.utils.encoding import force_unicode, smart_str

try:
    from boto.s3.connection import S3Connection, SubdomainCallingFormat
    from boto.exception import S3ResponseError
    from boto.s3.key import Key
except ImportError:
    raise ImproperlyConfigured("Could not load Boto's S3 bindings.\n"
                               "See http://code.google.com/p/boto/")

ACCESS_KEY_NAME     = getattr(settings, 'AWS_ACCESS_KEY_ID', None)
SECRET_KEY_NAME     = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None)
HEADERS             = getattr(settings, 'AWS_HEADERS', {})
STORAGE_BUCKET_NAME = getattr(settings, 'AWS_STORAGE_BUCKET_NAME', None)
AUTO_CREATE_BUCKET  = getattr(settings, 'AWS_AUTO_CREATE_BUCKET', False)
DEFAULT_ACL         = getattr(settings, 'AWS_DEFAULT_ACL', 'public-read')
BUCKET_ACL          = getattr(settings, 'AWS_BUCKET_ACL', DEFAULT_ACL)
QUERYSTRING_AUTH    = getattr(settings, 'AWS_QUERYSTRING_AUTH', True)
QUERYSTRING_EXPIRE  = getattr(settings, 'AWS_QUERYSTRING_EXPIRE', 3600)
REDUCED_REDUNDANCY  = getattr(settings, 'AWS_REDUCED_REDUNDANCY', False)
LOCATION            = getattr(settings, 'AWS_LOCATION', '')
ENCRYPTION          = getattr(settings, 'AWS_S3_ENCRYPTION', False)
CUSTOM_DOMAIN       = getattr(settings, 'AWS_S3_CUSTOM_DOMAIN', None)
CALLING_FORMAT      = getattr(settings, 'AWS_S3_CALLING_FORMAT', SubdomainCallingFormat())
SECURE_URLS         = getattr(settings, 'AWS_S3_SECURE_URLS', True)
FILE_NAME_CHARSET   = getattr(settings, 'AWS_S3_FILE_NAME_CHARSET', 'utf-8')
FILE_OVERWRITE      = getattr(settings, 'AWS_S3_FILE_OVERWRITE', True)
IS_GZIPPED          = getattr(settings, 'AWS_IS_GZIPPED', False)
PRELOAD_METADATA    = getattr(settings, 'AWS_PRELOAD_METADATA', False)
GZIP_CONTENT_TYPES  = getattr(settings, 'GZIP_CONTENT_TYPES', (
    'text/css',
    'application/javascript',
    'application/x-javascript'
))

if IS_GZIPPED:
    from gzip import GzipFile

def safe_join(base, *paths):
    """
    A version of django.utils._os.safe_join for S3 paths.

    Joins one or more path components to the base path component intelligently.
    Returns a normalized version of the final path.

    The final path must be located inside of the base path component (otherwise
    a ValueError is raised).

    Paths outside the base path indicate a possible security sensitive operation.
    """
    from urlparse import urljoin
    base_path = force_unicode(base)
    paths = map(lambda p: force_unicode(p), paths)
    final_path = urljoin(base_path + ("/" if not base_path.endswith("/") else ""), *paths)
    # Ensure final_path starts with base_path and that the next character after
    # the final path is '/' (or nothing, in which case final_path must be
    # equal to base_path).
    base_path_len = len(base_path)
    if not final_path.startswith(base_path) \
       or final_path[base_path_len:base_path_len+1] not in ('', '/'):
        raise ValueError('the joined path is located outside of the base path'
                         ' component')
    return final_path

class S3BotoStorage(Storage):
    """Amazon Simple Storage Service using Boto"""

    def __init__(self, bucket=STORAGE_BUCKET_NAME, access_key=None,
                       secret_key=None, bucket_acl=BUCKET_ACL, acl=DEFAULT_ACL, headers=HEADERS,
                       gzip=IS_GZIPPED, gzip_content_types=GZIP_CONTENT_TYPES,
                       querystring_auth=QUERYSTRING_AUTH, querystring_expire=QUERYSTRING_EXPIRE,
                       reduced_redundancy=REDUCED_REDUNDANCY, encryption=ENCRYPTION,
                       custom_domain=CUSTOM_DOMAIN, secure_urls=SECURE_URLS,
                       location=LOCATION, file_name_charset=FILE_NAME_CHARSET,
                       preload_metadata=PRELOAD_METADATA, calling_format=CALLING_FORMAT):
        self.bucket_acl = bucket_acl
        self.bucket_name = bucket
        self.acl = acl
        self.headers = headers
        self.preload_metadata = preload_metadata
        self.gzip = gzip
        self.gzip_content_types = gzip_content_types
        self.querystring_auth = querystring_auth
        self.querystring_expire = querystring_expire
        self.reduced_redundancy = reduced_redundancy
        self.encryption = encryption
        self.custom_domain = custom_domain
        self.secure_urls = secure_urls
        self.location = location or ''
        self.location = self.location.lstrip('/')
        self.file_name_charset = file_name_charset

        if not access_key and not secret_key:
             access_key, secret_key = self._get_access_keys()

        self.connection = S3Connection(access_key, secret_key, calling_format=calling_format)
        self._entries = {}

    @property
    def bucket(self):
        if not hasattr(self, '_bucket'):
            self._bucket = self._get_or_create_bucket(self.bucket_name)
        return self._bucket

    @property
    def entries(self):
        if self.preload_metadata and not self._entries:
            self._entries = dict((self._decode_name(entry.key), entry)
                                for entry in self.bucket.list())
        return self._entries

    def _get_access_keys(self):
        access_key = ACCESS_KEY_NAME
        secret_key = SECRET_KEY_NAME
        if (access_key or secret_key) and (not access_key or not secret_key):
            access_key = os.environ.get(ACCESS_KEY_NAME)
            secret_key = os.environ.get(SECRET_KEY_NAME)

        if access_key and secret_key:
            # Both were provided, so use them
            return access_key, secret_key

        return None, None

    def _get_or_create_bucket(self, name):
        """Retrieves a bucket if it exists, otherwise creates it."""
        try:
            return self.connection.get_bucket(name, validate=AUTO_CREATE_BUCKET)
        except S3ResponseError, e:
            if AUTO_CREATE_BUCKET:
                bucket = self.connection.create_bucket(name)
                bucket.set_acl(self.bucket_acl)
                return bucket
            raise ImproperlyConfigured, ("Bucket specified by "
            "AWS_STORAGE_BUCKET_NAME does not exist. Buckets can be "
            "automatically created by setting AWS_AUTO_CREATE_BUCKET=True")

    def _clean_name(self, name):
        # Useful for windows' paths
        return os.path.normpath(name).replace('\\', '/')

    def _normalize_name(self, name):
        try:
            return safe_join(self.location, name).lstrip('/')
        except ValueError:
            raise SuspiciousOperation("Attempted access to '%s' denied." % name)

    def _encode_name(self, name):
        return smart_str(name, encoding=self.file_name_charset)

    def _decode_name(self, name):
        return force_unicode(name, encoding=self.file_name_charset)

    def _compress_content(self, content):
        """Gzip a given string."""
        zbuf = StringIO()
        zfile = GzipFile(mode='wb', compresslevel=6, fileobj=zbuf)
        zfile.write(content.read())
        zfile.close()
        content.file = zbuf
        return content

    def _open(self, name, mode='rb'):
        name = self._normalize_name(self._clean_name(name))
        f = S3BotoStorageFile(name, mode, self)
        if not f.key:
            raise IOError('File does not exist: %s' % name)
        return f

    def _save(self, name, content):
        cleaned_name = self._clean_name(name)
        name = self._normalize_name(cleaned_name)
        headers = self.headers.copy()
        content_type = getattr(content,'content_type', mimetypes.guess_type(name)[0] or Key.DefaultContentType)

        if self.gzip and content_type in self.gzip_content_types:
            content = self._compress_content(content)
            headers.update({'Content-Encoding': 'gzip'})

        content.name = cleaned_name
        k = self.bucket.get_key(self._encode_name(name))
        if not k:
            k = self.bucket.new_key(self._encode_name(name))

        k.set_metadata('Content-Type',content_type)
        k.set_contents_from_file(content, headers=headers, policy=self.acl,
                                 reduced_redundancy=self.reduced_redundancy,
                                 encrypt_key=self.encryption)
        return cleaned_name

    def delete(self, name):
        name = self._normalize_name(self._clean_name(name))
        self.bucket.delete_key(self._encode_name(name))

    def exists(self, name):
        name = self._normalize_name(self._clean_name(name))
        if self.entries:
            return name in self.entries
        k = self.bucket.new_key(self._encode_name(name))
        return k.exists()

    def listdir(self, name):
        name = self._normalize_name(self._clean_name(name))
        dirlist = self.bucket.list(self._encode_name(name))
        files = []
        dirs = set()
        base_parts = name.split("/") if name else []
        for item in dirlist:
            parts = item.name.split("/")
            parts = parts[len(base_parts):]
            if len(parts) == 1:
                # File
                files.append(parts[0])
            elif len(parts) > 1:
                # Directory
                dirs.add(parts[0])
        return list(dirs),files

    def size(self, name):
        name = self._normalize_name(self._clean_name(name))
        if self.entries:
            entry = self.entries.get(name)
            if entry:
                return entry.size
            return 0
        return self.bucket.get_key(self._encode_name(name)).size

    def modified_time(self, name):
        try:
           from dateutil import parser, tz
        except ImportError:
            raise NotImplementedError()
        name = self._normalize_name(self._clean_name(name))
        entry = self.entries.get(name)
        # only call self.bucket.get_key() if the key is not found
        # in the preloaded metadata.
        if entry is None:
            entry = self.bucket.get_key(self._encode_name(name))
        # convert to string to date
        last_modified_date = parser.parse(entry.last_modified)
        # if the date has no timzone, assume UTC
        if last_modified_date.tzinfo == None:
            last_modified_date = last_modified_date.replace(tzinfo=tz.tzutc())
        # convert date to local time w/o timezone
        return last_modified_date.astimezone(tz.tzlocal()).replace(tzinfo=None)

    def url(self, name):
        name = self._normalize_name(self._clean_name(name))
        if self.custom_domain:
            return "%s://%s/%s" % ('https' if self.secure_urls else 'http', self.custom_domain, name)
        else:
            return self.connection.generate_url(self.querystring_expire, method='GET', \
                    bucket=self.bucket.name, key=self._encode_name(name), query_auth=self.querystring_auth, \
                    force_http=not self.secure_urls)

    def get_available_name(self, name):
        """ Overwrite existing file with the same name. """
        if FILE_OVERWRITE:
            name = self._clean_name(name)
            return name
        return super(S3BotoStorage, self).get_available_name(name)

class S3BotoStorageFile(File):
    def __init__(self, name, mode, storage):
        self._storage = storage
        self.name = name[len(self._storage.location):].lstrip('/')
        self._mode = mode
        self.key = storage.bucket.get_key(self._storage._encode_name(name))
        self._is_dirty = False
        self._file = None

    @property
    def size(self):
        return self.key.size

    @property
    def file(self):
        if self._file is None:
            self._file = StringIO()
            if 'r' in self._mode:
                self._is_dirty = False
                self.key.get_contents_to_file(self._file)
                self._file.seek(0)
        return self._file

    def read(self, *args, **kwargs):
        if 'r' not in self._mode:
            raise AttributeError("File was not opened in read mode.")
        return super(S3BotoStorageFile, self).read(*args, **kwargs)

    def write(self, *args, **kwargs):
        if 'w' not in self._mode:
            raise AttributeError("File was opened for read-only access.")
        self._is_dirty = True
        return super(S3BotoStorageFile, self).write(*args, **kwargs)

    def close(self):
        if self._is_dirty:
            self.key.set_contents_from_file(self._file, headers=self._storage.headers, policy=self._storage.acl)
        self.key.close()
<|endoftext|>


<commit_before>#countdown
def countdown_from_to(start,stop):
    if start<stop:
        print "Blastoff!"
    else:
        print start
        countdown_from_to(start-1,stop)

def main():
    countdown_from_to(156,99)

main()

#countup
def countup_from_to(start,stop):
    if start>stop:
        print "Blastoff!"
    else:
        print start
        countup_from_to(start+1,stop)

def main():
    countup_from_to(2,53)

main()

#adder
def adder(total,next):
    print "Running total: ", float(total)
    next = float(raw_input("Next number: "))
    total = total+next

def main():

    return
main()

<commit_msg>Added changes to recursion.py<commit_after>#countdown
def countdown(n):
	if n<0:
	    print "Blastoff!"
    else:
        print n
        countup(n+1)

def main():
    countdown(23)

main()

#countup

def countup(n):
    if n>10:
        print "Blastoff!"
    else:
        print n
        countup(n+1)

def main():
    countup(-300)

main()

#countdownfrom
def countdown_from_to(start,stop):
    if start<stop:
        print "Blastoff!"
    else:
        print start
        countdown_from_to(start-1,stop)

def main():
    countdown_from_to(156,99)

main()

#countupfrom
def countup_from_to(start,stop):
    if start>stop:
        print "Blastoff!"
    else:
        print start
        countup_from_to(start+1,stop)

def main():
    countup_from_to(2,53)

main()

#adder

<|endoftext|>