<commit_before>#!/usr/bin/env python

#----------------------------------------------------------------------
# Copyright (c) 2012-2015 Raytheon BBN Technologies
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and/or hardware specification (the "Work") to
# deal in the Work without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Work, and to permit persons to whom the Work
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Work.
#
# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
# IN THE WORK.
#----------------------------------------------------------------------
"""
Framework to run a GENI Aggregate Manager. See geni/am for the
Reference Aggregate Manager that this runs.

Run with "-h" flag to see usage and command line options.
"""

import sys

# Check python version. Requires 2.6 or greater, but less than 3.
if sys.version_info < (2, 6):
    raise Exception('Must use python 2.6 or greater.')
elif sys.version_info >= (3,):
    raise Exception('Not python 3 ready')

import logging
import optparse
import os

from gcf import geni
import gcf.geni.am
import gcf.geni.am.am2
import gcf.geni.am.am3
from gcf.geni.config import read_config
from gcf.geni.auth.util import getInstanceFromClassname

def parse_args(argv):
    parser = optparse.OptionParser()
    parser.add_option("-k", "--keyfile",
                      help="AM key file name", metavar="FILE")
    parser.add_option("-g", "--certfile",
                      help="AM certificate file name (PEM format)", metavar="FILE")
    parser.add_option("-c", "--configfile",  help="config file path", metavar="FILE")
    # Note: The trusted CH certificates are _not_ enough here.
    # It needs self signed certificates. EG CA certificates.
    parser.add_option("-r", "--rootcadir",
                      help="Trusted Root certificates directory (files in PEM format)", metavar="FILE")
    # Could try to determine the real IP Address instead of the loopback
    # using socket.gethostbyname(socket.gethostname())
    parser.add_option("-H", "--host",
                      help="server ip", metavar="HOST")
    parser.add_option("-p", "--port", type=int,
                      help="server port", metavar="PORT")
    parser.add_option("--debug", action="store_true", default=False,
                       help="enable debugging output")
    parser.add_option("-V", "--api-version", type=int,
                      help="AM API Version", default=2)
    parser.add_option("-D", "--delegate", metavar="DELEGATE",
                      help="Delegate to instanciate")
    return parser.parse_args()

def getAbsPath(path):
    """Return None or a normalized absolute path version of the argument string.
    Does not check that the path exists."""
    if path is None:
        return None
    if path.strip() == "":
        return None
    path = os.path.normcase(os.path.expanduser(path))
    if os.path.isabs(path):
        return path
    else:
        return os.path.abspath(path)

def main(argv=None):
    if argv is None:
        argv = sys.argv
    opts = parse_args(argv)[0]
    level = logging.INFO
    if opts.debug:
        level = logging.DEBUG
    logging.basicConfig(level=level)

    # Read in config file options, command line gets priority
    optspath = None
    if not opts.configfile is None:
        optspath = os.path.expanduser(opts.configfile)

    config = read_config(optspath)

    for (key,val) in config['aggregate_manager'].items():
        if hasattr(opts,key) and getattr(opts,key) is None:
            setattr(opts,key,val)
        if not hasattr(opts,key):
            setattr(opts,key,val)
    if getattr(opts,'rootcadir') is None:
        setattr(opts,'rootcadir',config['global']['rootcadir'])

    if opts.rootcadir is None:
        sys.exit('Missing path to trusted root certificate directory (-r argument)')

    certfile = getAbsPath(opts.certfile)
    keyfile = getAbsPath(opts.keyfile)
    if not os.path.exists(certfile):
        sys.exit("Aggregate certfile %s doesn't exist" % certfile)
    if not os.path.getsize(certfile) > 0:
        sys.exit("Aggregate certfile %s is empty" % certfile)

    if not os.path.exists(keyfile):
        sys.exit("Aggregate keyfile %s doesn't exist" % keyfile)
    if not os.path.getsize(keyfile) > 0:
        sys.exit("Aggregate keyfile %s is empty" % keyfile)

    # Instantiate an argument guard that will reject or modify
    # arguments and options provided to calls
    argument_guard = None
    if hasattr(opts, 'argument_guard'):
        argument_guard = getInstanceFromClassname(opts.argument_guard)

    # Instantiate authorizer from 'authorizer' config argument
    # By default, use the SFA authorizer
    if hasattr(opts, 'authorizer'):
        authorizer_classname = opts.authorizer
    else:
        authorizer_classname = "gcf.geni.auth.sfa_authorizer.SFA_Authorizer"
    authorizer = getInstanceFromClassname(authorizer_classname,
                                          getAbsPath(opts.rootcadir), opts, argument_guard)

    # Use XMLRPC authorizer if opt.remote_authorizer is set
    if hasattr(opts, 'remote_authorizer'):
        import xmlrpclib
        authorizer = xmlrpclib.Server(opts.remote_authorizer)

    # Instantiate resource manager from 'authorizer_resource_manager'
    # config argument. Default = None
    resource_manager = None
    if hasattr(opts, 'authorizer_resource_manager'):
        resource_manager = \
            getInstanceFromClassname(opts.authorizer_resource_manager)

    delegate=None
    if hasattr(opts, 'delegate'):
        try:
            delegate = \
                       getInstanceFromClassname(opts.delegate,
                                                getAbsPath(opts.rootcadir),
                                                config['global']['base_name'],
                                                "https://%s:%d/" % (opts.host, int(opts.port)),
                                                **vars(opts)
                                            )
        except AttributeError, e:
            msg="Could not create delegate from name %s: probably not a valid python class name. "%opts.delegate
            msg+=e.message
            logging.getLogger('gcf-am').error(msg)
            sys.exit(msg)

    # here rootcadir is supposed to be a single file with multiple
    # certs possibly concatenated together
    comboCertsFile = geni.CredentialVerifier.getCAsFileFromDir(getAbsPath(opts.rootcadir))

    if opts.api_version == 1:
        # rootcadir is dir of multiple certificates
        delegate = geni.ReferenceAggregateManager(getAbsPath(opts.rootcadir))
        ams = geni.AggregateManagerServer((opts.host, int(opts.port)),
                                          delegate=delegate,
                                          keyfile=keyfile,
                                          certfile=certfile,
                                          ca_certs=comboCertsFile,
                                          base_name=config['global']['base_name'])
    elif opts.api_version == 2:
        ams = gcf.geni.am.am2.AggregateManagerServer((opts.host, int(opts.port)),
                                                     keyfile=keyfile,
                                                     certfile=certfile,
                                                     trust_roots_dir=getAbsPath(opts.rootcadir),
                                                     ca_certs=comboCertsFile,
                                                     base_name=config['global']['base_name'],
                                                     authorizer=authorizer,
                                                     resource_manager=resource_manager)
    elif opts.api_version == 3:
        ams = gcf.geni.am.am3.AggregateManagerServer((opts.host, int(opts.port)),
                                                     keyfile=keyfile,
                                                     certfile=certfile,
                                                     trust_roots_dir=getAbsPath(opts.rootcadir),
                                                     ca_certs=comboCertsFile,
                                                     base_name=config['global']['base_name'],
                                                     authorizer=authorizer,
                                                     resource_manager=resource_manager,
                                                     delegate=delegate)
    else:
        msg = "Unknown API version: %d. Valid choices are \"1\", \"2\", or \"3\""
        sys.exit(msg % (opts.api_version))

    logging.getLogger('gcf-am').info('GENI AM (v%s) Listening on port %s...' % (opts.api_version, opts.port))
    ams.serve_forever()

if __name__ == "__main__":
    sys.exit(main())
<commit_msg>Got surprised with the way OptionParse creates a empty option entry for options that were not defined. Now I understand :ahelsing's remarks on the pull request<commit_after>#!/usr/bin/env python

#----------------------------------------------------------------------
# Copyright (c) 2012-2015 Raytheon BBN Technologies
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and/or hardware specification (the "Work") to
# deal in the Work without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Work, and to permit persons to whom the Work
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Work.
#
# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
# IN THE WORK.
#----------------------------------------------------------------------
"""
Framework to run a GENI Aggregate Manager. See geni/am for the
Reference Aggregate Manager that this runs.

Run with "-h" flag to see usage and command line options.
"""

import sys

# Check python version. Requires 2.6 or greater, but less than 3.
if sys.version_info < (2, 6):
    raise Exception('Must use python 2.6 or greater.')
elif sys.version_info >= (3,):
    raise Exception('Not python 3 ready')

import logging
import optparse
import os

from gcf import geni
import gcf.geni.am
import gcf.geni.am.am2
import gcf.geni.am.am3
from gcf.geni.config import read_config
from gcf.geni.auth.util import getInstanceFromClassname

def parse_args(argv):
    parser = optparse.OptionParser()
    parser.add_option("-k", "--keyfile",
                      help="AM key file name", metavar="FILE")
    parser.add_option("-g", "--certfile",
                      help="AM certificate file name (PEM format)", metavar="FILE")
    parser.add_option("-c", "--configfile",  help="config file path", metavar="FILE")
    # Note: The trusted CH certificates are _not_ enough here.
    # It needs self signed certificates. EG CA certificates.
    parser.add_option("-r", "--rootcadir",
                      help="Trusted Root certificates directory (files in PEM format)", metavar="FILE")
    # Could try to determine the real IP Address instead of the loopback
    # using socket.gethostbyname(socket.gethostname())
    parser.add_option("-H", "--host",
                      help="server ip", metavar="HOST")
    parser.add_option("-p", "--port", type=int,
                      help="server port", metavar="PORT")
    parser.add_option("--debug", action="store_true", default=False,
                       help="enable debugging output")
    parser.add_option("-V", "--api-version", type=int,
                      help="AM API Version", default=2)
    parser.add_option("-D", "--delegate", metavar="DELEGATE",
                      help="Delegate to instanciate")
    return parser.parse_args()

def getAbsPath(path):
    """Return None or a normalized absolute path version of the argument string.
    Does not check that the path exists."""
    if path is None:
        return None
    if path.strip() == "":
        return None
    path = os.path.normcase(os.path.expanduser(path))
    if os.path.isabs(path):
        return path
    else:
        return os.path.abspath(path)

def main(argv=None):
    if argv is None:
        argv = sys.argv
    opts = parse_args(argv)[0]
    level = logging.INFO
    if opts.debug:
        level = logging.DEBUG
    logging.basicConfig(level=level)

    # Read in config file options, command line gets priority
    optspath = None
    if not opts.configfile is None:
        optspath = os.path.expanduser(opts.configfile)

    config = read_config(optspath)

    for (key,val) in config['aggregate_manager'].items():
        if hasattr(opts,key) and getattr(opts,key) is None:
            setattr(opts,key,val)
        if not hasattr(opts,key):
            setattr(opts,key,val)
    if getattr(opts,'rootcadir') is None:
        setattr(opts,'rootcadir',config['global']['rootcadir'])

    if opts.rootcadir is None:
        sys.exit('Missing path to trusted root certificate directory (-r argument)')

    certfile = getAbsPath(opts.certfile)
    keyfile = getAbsPath(opts.keyfile)
    if not os.path.exists(certfile):
        sys.exit("Aggregate certfile %s doesn't exist" % certfile)
    if not os.path.getsize(certfile) > 0:
        sys.exit("Aggregate certfile %s is empty" % certfile)

    if not os.path.exists(keyfile):
        sys.exit("Aggregate keyfile %s doesn't exist" % keyfile)
    if not os.path.getsize(keyfile) > 0:
        sys.exit("Aggregate keyfile %s is empty" % keyfile)

    # Instantiate an argument guard that will reject or modify
    # arguments and options provided to calls
    argument_guard = None
    if hasattr(opts, 'argument_guard'):
        argument_guard = getInstanceFromClassname(opts.argument_guard)

    # Instantiate authorizer from 'authorizer' config argument
    # By default, use the SFA authorizer
    if hasattr(opts, 'authorizer'):
        authorizer_classname = opts.authorizer
    else:
        authorizer_classname = "gcf.geni.auth.sfa_authorizer.SFA_Authorizer"
    authorizer = getInstanceFromClassname(authorizer_classname,
                                          getAbsPath(opts.rootcadir), opts, argument_guard)

    # Use XMLRPC authorizer if opt.remote_authorizer is set
    if hasattr(opts, 'remote_authorizer'):
        import xmlrpclib
        authorizer = xmlrpclib.Server(opts.remote_authorizer)

    # Instantiate resource manager from 'authorizer_resource_manager'
    # config argument. Default = None
    resource_manager = None
    if hasattr(opts, 'authorizer_resource_manager'):
        resource_manager = \
            getInstanceFromClassname(opts.authorizer_resource_manager)

    delegate=None
    if hasattr(opts, 'delegate') and opts.delegate is not None and str(opts.delegate).strip() != "":
        try:
            delegate = \
                       getInstanceFromClassname(opts.delegate,
                                                getAbsPath(opts.rootcadir),
                                                config['global']['base_name'],
                                                "https://%s:%d/" % (opts.host, int(opts.port)),
                                                **vars(opts)
                                            )
        except AttributeError, e:
            msg="Could not create delegate from name %s: probably not a valid python class name. "%opts.delegate
            msg+=e.message
            logging.getLogger('gcf-am').error(msg)
            sys.exit(msg)

    # here rootcadir is supposed to be a single file with multiple
    # certs possibly concatenated together
    comboCertsFile = geni.CredentialVerifier.getCAsFileFromDir(getAbsPath(opts.rootcadir))

    if opts.api_version == 1:
        # rootcadir is dir of multiple certificates
        delegate = geni.ReferenceAggregateManager(getAbsPath(opts.rootcadir))
        ams = geni.AggregateManagerServer((opts.host, int(opts.port)),
                                          delegate=delegate,
                                          keyfile=keyfile,
                                          certfile=certfile,
                                          ca_certs=comboCertsFile,
                                          base_name=config['global']['base_name'])
    elif opts.api_version == 2:
        ams = gcf.geni.am.am2.AggregateManagerServer((opts.host, int(opts.port)),
                                                     keyfile=keyfile,
                                                     certfile=certfile,
                                                     trust_roots_dir=getAbsPath(opts.rootcadir),
                                                     ca_certs=comboCertsFile,
                                                     base_name=config['global']['base_name'],
                                                     authorizer=authorizer,
                                                     resource_manager=resource_manager)
    elif opts.api_version == 3:
        ams = gcf.geni.am.am3.AggregateManagerServer((opts.host, int(opts.port)),
                                                     keyfile=keyfile,
                                                     certfile=certfile,
                                                     trust_roots_dir=getAbsPath(opts.rootcadir),
                                                     ca_certs=comboCertsFile,
                                                     base_name=config['global']['base_name'],
                                                     authorizer=authorizer,
                                                     resource_manager=resource_manager,
                                                     delegate=delegate)
    else:
        msg = "Unknown API version: %d. Valid choices are \"1\", \"2\", or \"3\""
        sys.exit(msg % (opts.api_version))

    logging.getLogger('gcf-am').info('GENI AM (v%s) Listening on port %s...' % (opts.api_version, opts.port))
    ams.serve_forever()

if __name__ == "__main__":
    sys.exit(main())
<|endoftext|>


<commit_before>"""
Base classes used for the generation of code based on the model objects.
"""

from ..model.base import (SqlString, SqlSet)
from ..model.schema import (View, Table, Constraint, NamedConstraint,
    SqlConstraint, LanguageConstraint, ValueTypeValue, ColumnarSchemaObject)
from .base import (SchemaScriptGenerator)
import time

PLATFORMS = ('mysql',)

class MySqlScriptGenerator(SchemaScriptGenerator):
    """
    Generates MySql syntax for schema generation.
    """

    def __init__(self):
        SchemaScriptGenerator.__init__(self)

    def is_platform(self, platforms):
        """
        Checks if this generator is one of the supported platform grammars.
        The "platforms" variable is produced by the Change.platforms property.

        :param platforms:
        :return: boolean
        """
        for plat in platforms:
            if plat.strip().lower() in PLATFORMS:
                return True
        return False

    def _header(self, schema_object):
        """
        Create the header comment for the schema file.
        """
        return '-- Schema for ' + schema_object.name + \
               '\n-- Generated on ' + time.asctime(time.gmtime(time.time())) + \
               '\n\n'

    def _generate_base_table(self, table):
        """
        Generate the creation script for a Table.

        http://dev.mysql.com/doc/refman/5.1/en/create-table.html

        :param table: Table
        :return: list(str)
        """
        assert isinstance(table, Table)

        # Note: do not use "IF NOT EXISTS", because that indicates upgrade.
        constraint_sql = ''
        sql = 'CREATE TABLE '
        if table.catalog_name:
            sql += _parse_name(table.catalog_name) + '.'
        if table.schema_name:
            sql += _parse_name(table.schema_name) + '.'
        sql += _parse_name(table.table_name)
        # Tablespace used?

        input_validations = []

        sql += ' (\n'
        first = True
        for col in table.columns:
            if first:
                first = False
                sql += '    '
            else:
                sql += '\n    , '
            sql += _parse_name(col.name) + ' ' + _parse_value_type(
                col.value_type)

            for cst in col.constraints:
                if (isinstance(cst, SqlConstraint) and
                        cst.constraint_type == 'inputvalidation'):
                    input_validations.append(cst)
                if cst.constraint_type == 'notnull':
                    sql += ' NOT NULL'
                elif (cst.constraint_type == 'nullable' or
                        cst.constraint_type == 'null'):
                    # print("null constraint")
                    sql += ' NULL'

            if col.default_value is not None:
                sql += ' DEFAULT ' + _escape_value_type_value(col.default_value)

            if col.auto_increment:
                sql += ' AUTO_INCREMENT'

            # TODO add COMMENT, COLUMN_FORMAT, STORAGE support
            for cst in col.constraints:
                constraint_sql += _generate_base_constraints(
                    table, [col], cst)

        # FIXME add clustered index table constraint checking

        for cst in table.constraints:
            assert isinstance(cst, Constraint)
            if (isinstance(cst, SqlConstraint) and
                    cst.constraint_type == 'inputvalidation'):
                input_validations.append(cst)
            else:
                constraint_sql += _generate_base_constraints(
                    table, cst.get_columns_by_names(table), cst)

        sql += constraint_sql + '\n)'

        # FIXME add table options

        # FIXME add partition options

        # FIXME make this selectable.  For now, we'll hard-code it for the
        # foreign key support.
        sql += ' ENGINE=INNODB;\n'

        if len(input_validations) > 0:
            sql += _generate_validation_triggers(table, input_validations)

        return [ self._header(table), sql ]

    def _generate_base_view(self, view):
        """
        Generate the creation script for a View.

        :param view:
        :return: list(str)
        """
        assert isinstance(view, View)

        sql = 'CREATE '
        if view.replace_if_exists:
            sql += 'OR REPLACE '
        sql_string = view.select_query.get_for_platform('mysql')
        if sql_string is None:
            raise Exception("no mysql support for view " + view.name)
        assert isinstance(sql_string, SqlString)
        sql += 'VIEW ' + view.name + ' AS\n' + sql_string.sql + ';\n'
        return [self._header(view), sql]

    def _generate_base_sequence(self, sequence):
        """
        Generate the creation script for a Sequence.

        :param sequence:
        :return: list(str)
        """
        raise Exception("not implemented")

    def _generate_base_procedure(self, procedure):
        """
        Generate the creation script for a Procedure.

        :param procedure:
        :return: list(str)
        """
        raise Exception("not implemented")

    def _generate_upgrade_sqlchange(self, sql_change):
        """
        Generates the upgrade sql for a SqlChange object.  This can be called
        if the platforms don't match.

        Default implementation just returns the sql text.

        :param sql_change:
        :return: list(str)
        """
        if self.is_platform(sql_change.platforms):
            return [sql_change.sql]
        else:
            return []

    def _generate_upgrade_table(self, table):
        """
        Generate the upgrade script for a Table.

        :param table:
        :return: list(str)
        """

        # FIXME include dropping then recreating the triggers.

        raise Exception("not implemented")

    def _generate_upgrade_view(self, view):
        """
        Generate the upgrade script for a View.

        :param view:
        :return: list(str)
        """
        raise Exception("not implemented")

    def _generate_upgrade_sequence(self, sequence):
        """
        Generate the upgrade script for a Sequence.

        :param sequence:
        :return: list(str)
        """
        raise Exception("not implemented")

    def _generate_upgrade_procedure(self, procedure):
        """
        Generate the upgrade script for a Procedure.

        :param procedure:
        :return: list(str)
        """
        raise Exception("not implemented")

def _escape_value_type_value(vtv):
    """

    :param vtv: ValueTypeValue
    :return: str
    """
    assert isinstance(vtv, ValueTypeValue)

    if vtv.str_value is not None:
        # FIXME look at proper escaping
        return "'" + vtv.str_value.replace("'", "''")
    elif vtv.boolean_value is not None:
        if vtv.boolean_value:
            return "1"
        else:
            return "0"
    elif vtv.computed_value is not None:
        return str(vtv.computed_value)
    elif vtv.date_value is not None:
        # FIXME see if we need proper conversion here
        return str(vtv.date_value)
    elif vtv.numeric_value is not None:
        return str(vtv.numeric_value)
    else:
        return 'NULL'

def _parse_value_type(value_type):
    return value_type.strip().upper()

def _parse_name(name):
    # TODO properly escape the name
    return name.strip()

def _parse_index_option(option):
    # TODO properly parse the option; for now, assume it's a string
    if isinstance(option, str):
        return option.strip()
    else:
        raise Exception("can only parse string index options")

def _generate_base_constraints(table, columns, ct):
    assert isinstance(ct, Constraint)
    constraint_sql = ''
    column_names = ','.join(
        [_parse_name(column.name) for column in columns])
    assert isinstance(ct, Constraint)
    if isinstance(ct, LanguageConstraint):
        # code generation constraint, not used in schema generation
        return ''
    if isinstance(ct, SqlConstraint):
        # For all the additional stuff that this tool doesn't
        # support
        if ct.constraint_type == 'native':
            s = ct.sql
            assert isinstance(s, SqlSet)
            return ('\n    , ' +
                    s.get_for_platform(PLATFORMS).sql)
        return ''

    name = None
    if isinstance(ct, NamedConstraint):
        name = ct.name

    if ct.constraint_type == 'fulltextindex':
        assert name is not None
        constraint_sql += ('\n    , FULLTEXT INDEX ' + name + ' (' +
                           column_names + ')')
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'spatialindex':
        assert name is not None
        constraint_sql += '\n    , SPATIAL INDEX ' + name + \
                          ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'uniqueindex':
        if name is None:
            raise Exception("No name defined for unique index")
        constraint_sql += '\n    , CONSTRAINT ' + name + \
                          ' UNIQUE INDEX'
        if 'using' in ct.details:
            constraint_sql += ' USING ' + ct.details['using']
        constraint_sql += ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'index':
        assert name is not None
        constraint_sql += '\n    , INDEX ' + name
        if 'using' in ct.details:
            constraint_sql += ' USING ' + ct.details['using']
        constraint_sql += ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'fulltextkey':
        assert name is not None
        constraint_sql += '\n    , FULLTEXT KEY ' + name + \
                          ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'spatialkey':
        assert name is not None
        constraint_sql += '\n    , SPATIAL KEY ' + name + \
                          ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'primarykey':
        assert name is not None
        constraint_sql += '\n    , CONSTRAINT ' + name + \
                          ' PRIMARY KEY'
        if 'using' in ct.details:
            constraint_sql += 'USING ' + ct.details['using']
        constraint_sql += ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'uniquekey':
        assert name is not None
        constraint_sql += '\n    , CONSTRAINT ' + name + \
                          ' UNIQUE KEY'
        if 'using' in ct.details:
            constraint_sql += ' USING ' + ct.details['using']
        constraint_sql += ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'key':
        assert name is not None
        constraint_sql += '\n    , KEY ' + name
        if 'using' in ct.details:
            constraint_sql += ' USING ' + ct.details['using']
        constraint_sql += ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'foreignkey':
        assert name is not None
        if (('column' not in ct.details and
                'columns' not in ct.details) or
                'table' not in ct.details):
            raise Exception("column and table must be in foreign "
                            "key; found in " + column_names + " in " +
                            table.table_name)
        constraint_sql += '\n    , FOREIGN KEY ' + name + ' (' + \
                          _parse_name(column_names) + \
                          ') REFERENCES ' + \
                          _parse_name(ct.details['table']) + ' ('
        if 'column' in ct.details:
            constraint_sql += _parse_name(ct.details['column'])
        elif 'columns' in ct.details:
            constraint_sql += ",".join(
                _parse_name(fc) for fc in ct.details['columns'])
        else:
            raise Exception("no column definition for foreignkey")
        constraint_sql += ')'

        if 'match' in ct.details:
            # TODO details value should be FULL, PARTIAL, or SIMPLE
            constraint_sql += ' MATCH ' + ct.details[
                'match'].upper()
        if 'delete' in ct.details:
            # TODO option should be RESTRICT, CASCADE, SET NULL,
            # or NO ACTION
            constraint_sql += ' ON DELETE ' + ct.details[
                'delete'].upper()
        if 'update' in ct.details:
            # TODO option should be RESTRICT, CASCADE, SET NULL,
            # or NO ACTION
            constraint_sql += ' ON UPDATE ' + ct.details[
                'update'].upper()

            # We allow other constraint types, because those could be used
            # by other databases or tools.
    return constraint_sql

def _generate_validation_triggers(table, csts):
    assert isinstance(table, ColumnarSchemaObject)
    assert len(csts) > 0

    checks = ""
    for cst in csts:
        assert isinstance(cst, SqlConstraint)
        msg = 'Input validation failed'
        if 'message' in cst.details:
            msg = cst.details['message']
        sset = cst.sql
        assert isinstance(sset, SqlSet)
        checks += ('    IF NOT (' + sset.get_for_platform(PLATFORMS).sql +
                ') THEN\n' +
                "        CALL ErrorMsg ('" + msg + "');\n"
                'END IF;\n')

    sql = ('delimiter //\n' +
         'CREATE TRIGGER insert_validation_' + table.name +
         '\n        BEFORE INSERT ON ' + table.name +
         '\n        FOR EACH ROW' +
         '\nBEGIN\n' + checks +
         'END; //\n' +
         'CREATE TRIGGER update_validation_' + table.name +
         '\n        BEFORE INSERT ON ' + table.name +
         '\n        FOR EACH ROW' +
         '\nBEGIN\n' + checks +
         'END; //' +
         '\ndelimiter ;')

    return sql

<commit_msg>Fix the trigger generation, which requires a bit of extra work to make it language agnostic.<commit_after>"""
Base classes used for the generation of code based on the model objects.
"""

from ..model.base import (SqlString, SqlSet)
from ..model.schema import (View, Table, Constraint, NamedConstraint,
    SqlConstraint, LanguageConstraint, ValueTypeValue, ColumnarSchemaObject)
from .base import (SchemaScriptGenerator)
import time

PLATFORMS = ('mysql',)

class MySqlScriptGenerator(SchemaScriptGenerator):
    """
    Generates MySql syntax for schema generation.
    """

    def __init__(self):
        SchemaScriptGenerator.__init__(self)

    def is_platform(self, platforms):
        """
        Checks if this generator is one of the supported platform grammars.
        The "platforms" variable is produced by the Change.platforms property.

        :param platforms:
        :return: boolean
        """
        for plat in platforms:
            if plat.strip().lower() in PLATFORMS:
                return True
        return False

    def _header(self, schema_object):
        """
        Create the header comment for the schema file.
        """
        return '-- Schema for ' + schema_object.name + \
               '\n-- Generated on ' + time.asctime(time.gmtime(time.time())) + \
               '\n\n'

    def _generate_base_table(self, table):
        """
        Generate the creation script for a Table.

        http://dev.mysql.com/doc/refman/5.1/en/create-table.html

        :param table: Table
        :return: list(str)
        """
        assert isinstance(table, Table)

        # Note: do not use "IF NOT EXISTS", because that indicates upgrade.
        constraint_sql = ''
        sql = 'CREATE TABLE '
        if table.catalog_name:
            sql += _parse_name(table.catalog_name) + '.'
        if table.schema_name:
            sql += _parse_name(table.schema_name) + '.'
        sql += _parse_name(table.table_name)
        # Tablespace used?

        input_validations = []

        sql += ' (\n'
        first = True
        for col in table.columns:
            if first:
                first = False
                sql += '    '
            else:
                sql += '\n    , '
            sql += _parse_name(col.name) + ' ' + _parse_value_type(
                col.value_type)

            for cst in col.constraints:
                if (isinstance(cst, SqlConstraint) and
                        cst.constraint_type == 'inputvalidation'):
                    input_validations.append(cst)
                if cst.constraint_type == 'notnull':
                    sql += ' NOT NULL'
                elif (cst.constraint_type == 'nullable' or
                        cst.constraint_type == 'null'):
                    # print("null constraint")
                    sql += ' NULL'

            if col.default_value is not None:
                sql += ' DEFAULT ' + _escape_value_type_value(col.default_value)

            if col.auto_increment:
                sql += ' AUTO_INCREMENT'

            # TODO add COMMENT, COLUMN_FORMAT, STORAGE support
            for cst in col.constraints:
                constraint_sql += _generate_base_constraints(
                    table, [col], cst)

        # FIXME add clustered index table constraint checking

        for cst in table.constraints:
            assert isinstance(cst, Constraint)
            if (isinstance(cst, SqlConstraint) and
                    cst.constraint_type == 'inputvalidation'):
                input_validations.append(cst)
            else:
                constraint_sql += _generate_base_constraints(
                    table, cst.get_columns_by_names(table), cst)

        sql += constraint_sql + '\n)'

        # FIXME add table options

        # FIXME add partition options

        # FIXME make this selectable.  For now, we'll hard-code it for the
        # foreign key support.
        sql += ' ENGINE=INNODB;\n'

        if len(input_validations) > 0:
            sql += _generate_validation_triggers(table, input_validations)

        return [ self._header(table), sql ]

    def _generate_base_view(self, view):
        """
        Generate the creation script for a View.

        :param view:
        :return: list(str)
        """
        assert isinstance(view, View)

        sql = 'CREATE '
        if view.replace_if_exists:
            sql += 'OR REPLACE '
        sql_string = view.select_query.get_for_platform('mysql')
        if sql_string is None:
            raise Exception("no mysql support for view " + view.name)
        assert isinstance(sql_string, SqlString)
        sql += 'VIEW ' + view.name + ' AS\n' + sql_string.sql + ';\n'
        return [self._header(view), sql]

    def _generate_base_sequence(self, sequence):
        """
        Generate the creation script for a Sequence.

        :param sequence:
        :return: list(str)
        """
        raise Exception("not implemented")

    def _generate_base_procedure(self, procedure):
        """
        Generate the creation script for a Procedure.

        :param procedure:
        :return: list(str)
        """
        raise Exception("not implemented")

    def _generate_upgrade_sqlchange(self, sql_change):
        """
        Generates the upgrade sql for a SqlChange object.  This can be called
        if the platforms don't match.

        Default implementation just returns the sql text.

        :param sql_change:
        :return: list(str)
        """
        if self.is_platform(sql_change.platforms):
            return [sql_change.sql]
        else:
            return []

    def _generate_upgrade_table(self, table):
        """
        Generate the upgrade script for a Table.

        :param table:
        :return: list(str)
        """

        # FIXME include dropping then recreating the triggers.

        raise Exception("not implemented")

    def _generate_upgrade_view(self, view):
        """
        Generate the upgrade script for a View.

        :param view:
        :return: list(str)
        """
        raise Exception("not implemented")

    def _generate_upgrade_sequence(self, sequence):
        """
        Generate the upgrade script for a Sequence.

        :param sequence:
        :return: list(str)
        """
        raise Exception("not implemented")

    def _generate_upgrade_procedure(self, procedure):
        """
        Generate the upgrade script for a Procedure.

        :param procedure:
        :return: list(str)
        """
        raise Exception("not implemented")

def _escape_value_type_value(vtv):
    """

    :param vtv: ValueTypeValue
    :return: str
    """
    assert isinstance(vtv, ValueTypeValue)

    if vtv.str_value is not None:
        # FIXME look at proper escaping
        return "'" + vtv.str_value.replace("'", "''")
    elif vtv.boolean_value is not None:
        if vtv.boolean_value:
            return "1"
        else:
            return "0"
    elif vtv.computed_value is not None:
        return str(vtv.computed_value)
    elif vtv.date_value is not None:
        # FIXME see if we need proper conversion here
        return str(vtv.date_value)
    elif vtv.numeric_value is not None:
        return str(vtv.numeric_value)
    else:
        return 'NULL'

def _parse_value_type(value_type):
    return value_type.strip().upper()

def _parse_name(name):
    # TODO properly escape the name
    return name.strip()

def _parse_index_option(option):
    # TODO properly parse the option; for now, assume it's a string
    if isinstance(option, str):
        return option.strip()
    else:
        raise Exception("can only parse string index options")

def _generate_base_constraints(table, columns, ct):
    assert isinstance(ct, Constraint)
    constraint_sql = ''
    column_names = ','.join(
        [_parse_name(column.name) for column in columns])
    assert isinstance(ct, Constraint)
    if isinstance(ct, LanguageConstraint):
        # code generation constraint, not used in schema generation
        return ''
    if isinstance(ct, SqlConstraint):
        # For all the additional stuff that this tool doesn't
        # support
        if ct.constraint_type == 'native':
            s = ct.sql
            assert isinstance(s, SqlSet)
            return ('\n    , ' +
                    s.get_for_platform(PLATFORMS).sql)
        return ''

    name = None
    if isinstance(ct, NamedConstraint):
        name = ct.name

    if ct.constraint_type == 'fulltextindex':
        assert name is not None
        constraint_sql += ('\n    , FULLTEXT INDEX ' + name + ' (' +
                           column_names + ')')
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'spatialindex':
        assert name is not None
        constraint_sql += '\n    , SPATIAL INDEX ' + name + \
                          ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'uniqueindex':
        if name is None:
            raise Exception("No name defined for unique index")
        constraint_sql += '\n    , CONSTRAINT ' + name + \
                          ' UNIQUE INDEX'
        if 'using' in ct.details:
            constraint_sql += ' USING ' + ct.details['using']
        constraint_sql += ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'index':
        assert name is not None
        constraint_sql += '\n    , INDEX ' + name
        if 'using' in ct.details:
            constraint_sql += ' USING ' + ct.details['using']
        constraint_sql += ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'fulltextkey':
        assert name is not None
        constraint_sql += '\n    , FULLTEXT KEY ' + name + \
                          ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'spatialkey':
        assert name is not None
        constraint_sql += '\n    , SPATIAL KEY ' + name + \
                          ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'primarykey':
        assert name is not None
        constraint_sql += '\n    , CONSTRAINT ' + name + \
                          ' PRIMARY KEY'
        if 'using' in ct.details:
            constraint_sql += 'USING ' + ct.details['using']
        constraint_sql += ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'uniquekey':
        assert name is not None
        constraint_sql += '\n    , CONSTRAINT ' + name + \
                          ' UNIQUE KEY'
        if 'using' in ct.details:
            constraint_sql += ' USING ' + ct.details['using']
        constraint_sql += ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'key':
        assert name is not None
        constraint_sql += '\n    , KEY ' + name
        if 'using' in ct.details:
            constraint_sql += ' USING ' + ct.details['using']
        constraint_sql += ' (' + _parse_name(column_names) + ')'
        if 'option' in ct.details:
            constraint_sql += ' ' + _parse_index_option(
                ct.details['option'])

    elif ct.constraint_type == 'foreignkey':
        assert name is not None
        if (('column' not in ct.details and
                'columns' not in ct.details) or
                'table' not in ct.details):
            raise Exception("column and table must be in foreign "
                            "key; found in " + column_names + " in " +
                            table.table_name)
        constraint_sql += '\n    , FOREIGN KEY ' + name + ' (' + \
                          _parse_name(column_names) + \
                          ') REFERENCES ' + \
                          _parse_name(ct.details['table']) + ' ('
        if 'column' in ct.details:
            constraint_sql += _parse_name(ct.details['column'])
        elif 'columns' in ct.details:
            constraint_sql += ",".join(
                _parse_name(fc) for fc in ct.details['columns'])
        else:
            raise Exception("no column definition for foreignkey")
        constraint_sql += ')'

        if 'match' in ct.details:
            # TODO details value should be FULL, PARTIAL, or SIMPLE
            constraint_sql += ' MATCH ' + ct.details[
                'match'].upper()
        if 'delete' in ct.details:
            # TODO option should be RESTRICT, CASCADE, SET NULL,
            # or NO ACTION
            constraint_sql += ' ON DELETE ' + ct.details[
                'delete'].upper()
        if 'update' in ct.details:
            # TODO option should be RESTRICT, CASCADE, SET NULL,
            # or NO ACTION
            constraint_sql += ' ON UPDATE ' + ct.details[
                'update'].upper()

            # We allow other constraint types, because those could be used
            # by other databases or tools.
    return constraint_sql

def _generate_validation_triggers(table, csts):
    assert isinstance(table, ColumnarSchemaObject)
    assert len(csts) > 0

    checks = ""
    for cst in csts:
        assert isinstance(cst, SqlConstraint)
        msg = 'Input validation failed'
        if 'message' in cst.details:
            msg = cst.details['message']
        sset = cst.sql
        assert isinstance(sset, SqlSet)
        sval = sset.get_for_platform(PLATFORMS).sql

        for col in table.columns:
            sval = sval.replace('{' + col.name + '}', 'NEW.' + col.name)

        checks += ('    IF NOT (' + sval +
                ') THEN\n' +
                "        CALL ErrorMsg ('" + msg + "');\n"
                '    END IF;\n')

    sql = ('delimiter //\n' +
         'CREATE TRIGGER insert_validation_' + table.name +
         '\n        BEFORE INSERT ON ' + table.name +
         '\n        FOR EACH ROW' +
         '\nBEGIN\n' + checks +
         'END; //\n' +
         'CREATE TRIGGER update_validation_' + table.name +
         '\n        BEFORE UPDATE ON ' + table.name +
         '\n        FOR EACH ROW' +
         '\nBEGIN\n' + checks +
         'END; //' +
         '\ndelimiter ;')

    return sql

<|endoftext|>


<commit_before># -*-coding:Utf-8 -*

# Copyright (c) 2010 LE GOFF Vincent
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of the copyright holder nor the names of its contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""Fichier contenant la classe Attaque, détaillée plus bas.

Il contient également les autres classes héritées d'Attaque :
    Attaque
    Coup

"""

from abstraits.obase import BaseObj

class Attaque(BaseObj):

    """Classe représentant une attaque."""

    def __init__(self, personnage, cle):
        """Constructeur de l'attaque."""
        BaseObj.__init__(self)
        self.personnage = personnage
        self.cle = cle
        self.msg_tentative = {
            "moi": "",
            "contre": "",
            "autres": "",
        }
        self.msg_reussite = {
            "moi": "",
            "contre": "",
            "autres": "",
        }
        self._construire()

    def envoyer_msg_tentative(self, moi, contre, membre):
        """Envoie les messages en cas de tentative."""
        moi.envoyer_lisser(self.msg_tentative["moi"], moi=moi, contre=contre,
                membre=membre)

class Coup(Attaque):

    """Classe représentant un coup."""

    def __init__(self, personnage, cle):
        """Constructeur du coup."""
        Attaque.__init__(self, personnage, cle)
        self.msg_tentative["moi"] = "Vous tentez d'atteindre {contre} à {membre}."
        self.msg_tentative["contre"] = "{moi} tente de vous atteindre à {membre}."
        self.msg_tentative["autres"] = "{moi} tente d'atteindre {contre} à {membre}."

class CoupEpee(Attaque):

    """Classe représentant un coup d'épée."""

    def __init__(self, personnage):
        """Constructeur du coup."""
        Coup.__init__(self,  personnage, "coup_epee")
        self.msg<commit_msg>Légère extension du combat<commit_after># -*-coding:Utf-8 -*

# Copyright (c) 2010 LE GOFF Vincent
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of the copyright holder nor the names of its contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""Fichier contenant la classe Attaque, détaillée plus bas.

Il contient également les autres classes héritées d'Attaque :
    Attaque
    Coup

"""

from abstraits.obase import BaseObj

class Attaque(BaseObj):

    """Classe représentant une attaque."""

    def __init__(self, personnage, cle):
        """Constructeur de l'attaque."""
        BaseObj.__init__(self)
        self.personnage = personnage
        self.cle = cle
        self.msg_tentative = {
            "moi": "",
            "contre": "",
            "autres": "",
        }
        self.msg_reussite = {
            "moi": "",
            "contre": "",
            "autres": "",
        }
        self._construire()

    def envoyer_msg_tentative(self, moi, contre, membre):
        """Envoie les messages en cas de tentative."""
        moi.envoyer_lisser(self.msg_tentative["moi"], moi=moi, contre=contre,
                membre=membre)
        contre.envoyer_lisser(self.msg_tentative["contre"],  moi=moi,
                contre=contre, membre=membre)
        moi.salle.envoyer_lisser(self.msg_tentative["autres"],  moi=moi,
                contre=contre, membre=membre)

    def envoyer_msg_reussite(self, moi, contre, membre, degats):
        """Envoie les messages en cas de réussite."""
        moi.envoyer_lisser(self.msg_reussite["moi"], moi=moi, contre=contre,
                membre=membre, degats=degats)
        contre.envoyer_lisser(self.msg_reussite["contre"],  moi=moi,
                contre=contre, membre=membre, degats=degats)
        moi.salle.envoyer_lisser(self.msg_reussite["autres"],  moi=moi,
                contre=contre, membre=membre, degats)

class Coup(Attaque):

    """Classe représentant un coup."""

    def __init__(self, personnage, cle):
        """Constructeur du coup."""
        Attaque.__init__(self, personnage, cle)
        self.msg_tentative["moi"] = \
                "Vous tentez d'atteindre {contre} à {membre}."
        self.msg_tentative["contre"] = \
                "{moi} tente de vous atteindre à {membre}."
        self.msg_tentative["autres"] = \
                "{moi} tente d'atteindre {contre} à {membre}."
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import datetime
import os

from .check import is_iterable
from .convert import BYTE_PREFIXES, to_str
from .fs import fullpath
from .misc import is_plural

try:
    import bitmath
except ImportError:
    bitmath = None

def attributes(obj, ignore=None):
    if ignore is None:
        attrs = tuple(map(to_str, obj))
    else:
        ignored = ignore if is_iterable(ignore) else (ignore,)
        attrs = (to_str(x) for x in obj if x not in ignored)
    return attrs

def items(obj, ignore=None):
    if ignore is None:
        res = (f"{k}={v}" for k, v in obj.items())
    else:
        ignored = ignore if is_iterable(ignore) else (ignore,)
        res = (f"{k}={v}" for k, v in obj.items() if k not in ignored)
    return res

def path(*paths):
    return os.path.normcase(fullpath(os.path.join(*paths)))

path.from_iterable = lambda it: path(*it)

def size(obj):
    """
    formats size of bytes
    """
    value = float(obj)
    try:
        return bitmath.Byte(value).best_prefix()
    except AttributeError:
        for prefix in BYTE_PREFIXES[:-1]:
            if abs(value) < 1 << 10:
                return f"{value:3.2f} {prefix}"
            else:
                value >>= 10
        return f"{value:.2f} {BYTE_PREFIXES[-1]}"

def speed(obj):
    return f"{size(obj)}/s"

def time(obj):
    seconds = abs(int(obj))
    dt = datetime.datetime(1, 1, 1) + datetime.timedelta(seconds=seconds)
    days = dt.day - 1 if dt.day > 1 else 0

    timelist = []

    if days:
        timelist.append(f"{days} day" + ("s" if is_plural(days) else ""))

    timenames = ("hour", "minute", "second")
    for name in timenames:
        value = getattr(dt, name)
        if not value:
            continue
        timelist.append(f"{value} {name}" + ("s" if is_plural(value) else ""))

    return ", ".join(timelist)
<commit_msg>Fix format size routine<commit_after># -*- coding: utf-8 -*-

import datetime
import os

import bitmath

from .check import is_iterable
from .convert import BYTE_PREFIXES, to_str
from .fs import fullpath
from .misc import is_plural

def attributes(obj, ignore=None):
    if ignore is None:
        attrs = tuple(map(to_str, obj))
    else:
        ignored = ignore if is_iterable(ignore) else (ignore,)
        attrs = (to_str(x) for x in obj if x not in ignored)
    return attrs

def items(obj, ignore=None):
    if ignore is None:
        res = (f"{k}={v}" for k, v in obj.items())
    else:
        ignored = ignore if is_iterable(ignore) else (ignore,)
        res = (f"{k}={v}" for k, v in obj.items() if k not in ignored)
    return res

def path(*paths):
    return os.path.normcase(fullpath(os.path.join(*paths)))

path.from_iterable = lambda it: path(*it)

def size(value):
    """
    formats size of bytes
    """
    return bitmath.Byte(value).best_prefix()

def speed(obj):
    return f"{size(obj)}/s"

def time(obj):
    seconds = abs(int(obj))
    dt = datetime.datetime(1, 1, 1) + datetime.timedelta(seconds=seconds)
    days = dt.day - 1 if dt.day > 1 else 0

    timelist = []

    if days:
        timelist.append(f"{days} day" + ("s" if is_plural(days) else ""))

    timenames = ("hour", "minute", "second")
    for name in timenames:
        value = getattr(dt, name)
        if not value:
            continue
        timelist.append(f"{value} {name}" + ("s" if is_plural(value) else ""))

    return ", ".join(timelist)
<|endoftext|>


<commit_before># This file is part of the pyMOR project (http://www.pymor.org).
# Copyright Holders: Rene Milk, Stephan Rave, Felix Schindler
# License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

from __future__ import absolute_import, division, print_function

import numpy as np

from pymor.core import ImmutableInterface
from pymor.core.logger import getLogger
from pymor.la import NumpyVectorArray, NumpyVectorSpace, induced_norm
from pymor.la.gram_schmidt import gram_schmidt
from pymor.operators.basic import OperatorBase, LincombOperator
from pymor.operators.ei import EmpiricalInterpolatedOperator
from pymor.reductors.basic import GenericRBReconstructor

class ResidualOperator(OperatorBase):

    def __init__(self, operator, functional, name=None):
        self.source = operator.source
        self.range = operator.range
        self.linear = operator.linear
        self.operator = operator
        self.functional = functional
        self.name = name

    def apply(self, U, ind=None, mu=None):
        V = self.operator.apply(U, ind=ind, mu=mu)
        if self.functional:
            F = self.functional.as_vector(mu)
            if len(V) > 1:
                R = F.copy(ind=[0]*len(V)) - V
            else:
                R = F - V
        else:
            R = - V
        return R

    def projected_to_subbasis(self, dim_source=None, dim_range=None, name=None):
        return ResidualOperator(self.operator.projected_to_subbasis(dim_source, dim_range),
                                self.functional.projected_to_subbasis(dim_range, None),
                                name=name)

class NonProjectedResiudalOperator(ResidualOperator):

    def __init__(self, operator, functional, product):
        super(NonProjectedResiudalOperator, self).__init__(operator, functional)
        self.product = product

    def apply(self, U, ind=None, mu=None):
        R = super(NonProjectedResiudalOperator, self).apply(U, ind=ind, mu=mu)
        if self.product:
            R_riesz = self.product.apply_inverse(R)
            return R_riesz * (np.sqrt(R_riesz.dot(R, pairwise=True)) / R_riesz.l2_norm())[0]
        else:
            return R

    def projected_to_subbasis(self, dim_source=None, dim_range=None, name=None):
        product = self.product.projected_to_subbasis(dim_range, dim_range) if self.product is not None else None
        return ResidualOperator(self.operator.projected_to_subbasis(dim_source, dim_range),
                                self.functional.projected_to_subbasis(dim_range, None),
                                product,
                                name=name)

class NonProjectedReconstructor(ImmutableInterface):

    def __init__(self, product):
        self.norm = induced_norm(product) if product else None

    def reconstruct(self, U):
        if self.norm:
            return U * (U.l2_norm() / self.norm(U))[0]
        else:
            return U

def reduce_residual(operator, functional=None, RB=None, product=None, extends=None):
    """Generic reduced basis residual reductor.
    """
    assert functional == None \
        or functional.range == NumpyVectorSpace(1) and functional.source == operator.source and functional.linear
    assert RB is None or RB in operator.source
    assert product is None or product.source == product.range == operator.range
    assert extends is None or len(extends) == 3

    logger = getLogger('pymor.reductors.reduce_residual')

    if RB is None:
        RB = operator.source.empty()

    if extends and isinstance(extends[0], NonProjectedResiudalOperator):
        extends = None
    if extends:
        residual_range = extends[1].RB.copy()
        residual_range_dims = list(extends[2]['residual_range_dims'])
        ind_range = range(extends[0].source.dim, len(RB))
    else:
        residual_range = operator.range.empty()
        residual_range_dims = []
        ind_range = range(-1, len(RB))

    class CollectionError(Exception):
        def __init__(self, op):
            super(CollectionError, self).__init__()
            self.op = op

    def collect_operator_ranges(op, ind, residual_range):
        if isinstance(op, LincombOperator):
            for o in op.operators:
                collect_operator_ranges(o, ind, residual_range)
        elif isinstance(op, EmpiricalInterpolatedOperator):
            if hasattr(op, 'collateral_basis') and ind == -1:
                residual_range.append(op.collateral_basis)
        elif op.linear and not op.parametric:
            if ind >= 0:
                residual_range.append(op.apply(RB, ind=ind))
        else:
            raise CollectionError(op)

    def collect_functional_ranges(op, residual_range):
        if isinstance(op, LincombOperator):
            for o in op.operators:
                collect_functional_ranges(o, residual_range)
        elif op.linear and not op.parametric:
            residual_range.append(op.as_vector())
        else:
            raise CollectionError(op)

    for i in ind_range:
        logger.info('Computing residual range for basis vector {}...'.format(i))
        new_residual_range = operator.range.empty()
        try:
            if i == -1:
                collect_functional_ranges(functional, new_residual_range)
            collect_operator_ranges(operator, i, new_residual_range)
        except CollectionError as e:
            logger.warn('Cannot compute range of {}. Evaluation will be slow.'.format(e.op))
            operator = operator.projected(RB, None)
            return (NonProjectedResiudalOperator(operator, functional, product),
                    NonProjectedReconstructor(product),
                    {})

        if product:
            logger.info('Computing Riesz representatives for basis vector {}...'.format(i))
            new_residual_range = product.apply_inverse(new_residual_range)

        gram_schmidt_offset = len(residual_range)
        residual_range.append(new_residual_range)
        logger.info('Orthonormalizing ...')
        gram_schmidt(residual_range, offset=gram_schmidt_offset, product=product, copy=False)
        residual_range_dims.append(len(residual_range))

    logger.info('Projecting ...')
    operator = operator.projected(RB, residual_range, product=None)  # the product always cancels out.
    functional = functional.projected(residual_range, None, product=None)

    return (ResidualOperator(operator, functional),
            GenericRBReconstructor(residual_range),
            {'residual_range_dims': residual_range_dims})
<commit_msg>[reductors] make ResidualOperator more efficient<commit_after># This file is part of the pyMOR project (http://www.pymor.org).
# Copyright Holders: Rene Milk, Stephan Rave, Felix Schindler
# License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

from __future__ import absolute_import, division, print_function

import numpy as np

from pymor.core import ImmutableInterface
from pymor.core.logger import getLogger
from pymor.la import NumpyVectorArray, NumpyVectorSpace, induced_norm
from pymor.la.gram_schmidt import gram_schmidt
from pymor.operators.basic import OperatorBase, LincombOperator
from pymor.operators.ei import EmpiricalInterpolatedOperator
from pymor.reductors.basic import GenericRBReconstructor

class ResidualOperator(OperatorBase):

    def __init__(self, operator, functional, name=None):
        self.source = operator.source
        self.range = operator.range
        self.linear = operator.linear
        self.operator = operator
        self.functional = functional
        self.functional_vector = functional.as_vector() if functional and not functional.parametric else None
        self.name = name

    def apply(self, U, ind=None, mu=None):
        V = self.operator.apply(U, ind=ind, mu=mu)
        if self.functional:
            F = self.functional_vector or self.functional.as_vector(mu)
            if len(V) > 1:
                V.axpy(-1., F, x_ind=[0]*len(V))
            else:
                V.axpy(-1., F)
        return V

    def projected_to_subbasis(self, dim_source=None, dim_range=None, name=None):
        return ResidualOperator(self.operator.projected_to_subbasis(dim_source, dim_range),
                                self.functional.projected_to_subbasis(dim_range, None),
                                name=name)

class NonProjectedResiudalOperator(ResidualOperator):

    def __init__(self, operator, functional, product):
        super(NonProjectedResiudalOperator, self).__init__(operator, functional)
        self.product = product

    def apply(self, U, ind=None, mu=None):
        R = super(NonProjectedResiudalOperator, self).apply(U, ind=ind, mu=mu)
        if self.product:
            R_riesz = self.product.apply_inverse(R)
            return R_riesz * (np.sqrt(R_riesz.dot(R, pairwise=True)) / R_riesz.l2_norm())[0]
        else:
            return R

    def projected_to_subbasis(self, dim_source=None, dim_range=None, name=None):
        product = self.product.projected_to_subbasis(dim_range, dim_range) if self.product is not None else None
        return ResidualOperator(self.operator.projected_to_subbasis(dim_source, dim_range),
                                self.functional.projected_to_subbasis(dim_range, None),
                                product,
                                name=name)

class NonProjectedReconstructor(ImmutableInterface):

    def __init__(self, product):
        self.norm = induced_norm(product) if product else None

    def reconstruct(self, U):
        if self.norm:
            return U * (U.l2_norm() / self.norm(U))[0]
        else:
            return U

def reduce_residual(operator, functional=None, RB=None, product=None, extends=None):
    """Generic reduced basis residual reductor.
    """
    assert functional == None \
        or functional.range == NumpyVectorSpace(1) and functional.source == operator.source and functional.linear
    assert RB is None or RB in operator.source
    assert product is None or product.source == product.range == operator.range
    assert extends is None or len(extends) == 3

    logger = getLogger('pymor.reductors.reduce_residual')

    if RB is None:
        RB = operator.source.empty()

    if extends and isinstance(extends[0], NonProjectedResiudalOperator):
        extends = None
    if extends:
        residual_range = extends[1].RB.copy()
        residual_range_dims = list(extends[2]['residual_range_dims'])
        ind_range = range(extends[0].source.dim, len(RB))
    else:
        residual_range = operator.range.empty()
        residual_range_dims = []
        ind_range = range(-1, len(RB))

    class CollectionError(Exception):
        def __init__(self, op):
            super(CollectionError, self).__init__()
            self.op = op

    def collect_operator_ranges(op, ind, residual_range):
        if isinstance(op, LincombOperator):
            for o in op.operators:
                collect_operator_ranges(o, ind, residual_range)
        elif isinstance(op, EmpiricalInterpolatedOperator):
            if hasattr(op, 'collateral_basis') and ind == -1:
                residual_range.append(op.collateral_basis)
        elif op.linear and not op.parametric:
            if ind >= 0:
                residual_range.append(op.apply(RB, ind=ind))
        else:
            raise CollectionError(op)

    def collect_functional_ranges(op, residual_range):
        if isinstance(op, LincombOperator):
            for o in op.operators:
                collect_functional_ranges(o, residual_range)
        elif op.linear and not op.parametric:
            residual_range.append(op.as_vector())
        else:
            raise CollectionError(op)

    for i in ind_range:
        logger.info('Computing residual range for basis vector {}...'.format(i))
        new_residual_range = operator.range.empty()
        try:
            if i == -1:
                collect_functional_ranges(functional, new_residual_range)
            collect_operator_ranges(operator, i, new_residual_range)
        except CollectionError as e:
            logger.warn('Cannot compute range of {}. Evaluation will be slow.'.format(e.op))
            operator = operator.projected(RB, None)
            return (NonProjectedResiudalOperator(operator, functional, product),
                    NonProjectedReconstructor(product),
                    {})

        if product:
            logger.info('Computing Riesz representatives for basis vector {}...'.format(i))
            new_residual_range = product.apply_inverse(new_residual_range)

        gram_schmidt_offset = len(residual_range)
        residual_range.append(new_residual_range)
        logger.info('Orthonormalizing ...')
        gram_schmidt(residual_range, offset=gram_schmidt_offset, product=product, copy=False)
        residual_range_dims.append(len(residual_range))

    logger.info('Projecting ...')
    operator = operator.projected(RB, residual_range, product=None)  # the product always cancels out.
    functional = functional.projected(residual_range, None, product=None)

    return (ResidualOperator(operator, functional),
            GenericRBReconstructor(residual_range),
            {'residual_range_dims': residual_range_dims})
<|endoftext|>


<commit_before>from qgl2.qgl1 import QubitFactory, Id, X
from qgl2.qgl2 import qgl2decl, sequence, concur, seq
from qgl2.basic_sequences.qgl2_plumbing import init

@qgl2decl
def multiQbitTest() -> sequence:
    q1 = QubitFactory('q1')
    q2 = QubitFactory('q2')

    with concur:
        with seq:
            init(q1)
            Id(q1)
            X(q1)
        with seq:
            init(q2)
            X(q2)
            Id(q2)

@qgl2decl
def multiQbitTest2() -> sequence:
    q1 = QubitFactory('q1')
    q2 = QubitFactory('q2')

    with concur:
        for q in [q1, q2]:
            init(q)
            Id(q)
            X(q)
<commit_msg>add a measurement to my test so I see the digitizer<commit_after>from qgl2.qgl1 import QubitFactory, Id, X, MEAS
from qgl2.qgl2 import qgl2decl, sequence, concur, seq
from qgl2.basic_sequences.qgl2_plumbing import init

@qgl2decl
def multiQbitTest() -> sequence:
    q1 = QubitFactory('q1')
    q2 = QubitFactory('q2')

    with concur:
        with seq:
            init(q1)
            Id(q1)
            X(q1)
        with seq:
            init(q2)
            X(q2)
            Id(q2)

@qgl2decl
def multiQbitTest2() -> sequence:
    q1 = QubitFactory('q1')
    q2 = QubitFactory('q2')

    with concur:
        for q in [q1, q2]:
            init(q)
            Id(q)
            X(q)
            MEAS(q)
<|endoftext|>


<commit_before>#  Copyright 2008-2011 Nokia Siemens Networks Oyj
#
#  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 __future__ import with_statement
import os
import os.path
import re
import sys
import tempfile
import codecs

from robot.output import LOGGER
from robot.reporting.jsondatamodel import SeparatingWriter
from robot.version import get_full_version
from robot import utils

try:
    from org.robotframework.RobotRunner import getResourceAsStream
except ImportError:  # Occurs unless using robotframework.jar
    JarReader = None
else:
    from java.io import BufferedReader, InputStreamReader
    def JarReader(path):
        return BufferedReader(InputStreamReader(getResourceAsStream(path)))

class _Builder(object):

    def __init__(self, context):
        self._settings = context.settings
        self._context = context
        self._path = self._parse_file(self._type)

    def build(self):
        raise NotImplementedError(self.__class__.__name__)

    def _parse_file(self, name):
        value = self._settings[name]
        return value if value != 'NONE' else None

class OutputBuilder(_Builder):
    _type = 'Output'
    _temp_file = None

    def build(self):
        if self._path:
            self._context.result_from_xml.serialize_output(output_file)

class XUnitBuilder(_Builder):
    _type = 'XUnitFile'

    def build(self):
        if self._path:
            self._context.result_from_xml.serialize_xunit(self._path)

class _HTMLFileBuilder(_Builder):
    _type = NotImplemented
    _template = NotImplemented

    def build(self):
        if self._path:
            self._context.data_model.set_settings(self._get_settings())
            self._format_data()
            if self._write_file():
                LOGGER.output_file(self._type, self._path)

    def _url_from_path(self, source, destination):
        if not destination:
            return None
        return utils.get_link_path(destination, os.path.dirname(source))

    def _write_file(self):
        try:
            with codecs.open(self._path, 'w', encoding='UTF-8') as outfile:
                writer = HTMLFileWriter(outfile, self._context.data_model)
                for line in _WebContentFile(self._template):
                    writer.line(line)
        except EnvironmentError, err:
            LOGGER.error("Opening '%s' failed: %s"
                         % (err.filename, err.strerror))
            return False
        return True

class LogBuilder(_HTMLFileBuilder):
    _type = 'Log'
    _template = 'log.html'

    def _format_data(self):
        if self._context.data_model._split_results:
            self._write_split_tests()

    def _write_split_tests(self):
        basename = os.path.splitext(self._path)[0]
        for index, (keywords, strings) in enumerate(self._context.data_model._split_results):
            index += 1  # enumerate accepts start index only in Py 2.6+
            self._write_test(index, keywords, strings, '%s-%d.js' % (basename, index))

    def _write_test(self, index, keywords, strings, path):
        # TODO: Refactor heavily - ask Jussi or Peke for more details
        with codecs.open(path, 'w', encoding='UTF-8') as outfile:
            writer = SeparatingWriter(outfile, '')
            writer.dump_json('window.keywords%d = ' % index, keywords)
            writer.dump_json('window.strings%d = ' % index, strings)
            writer.write('window.fileLoading.notify("%s");\n' % os.path.basename(path))

    def _get_settings(self):
        return  {
            'title': self._settings['LogTitle'],
            'reportURL': self._url_from_path(self._path,
                                             self._parse_file('Report')),
            'splitLogBase': os.path.basename(os.path.splitext(self._path)[0])
        }

class ReportBuilder(_HTMLFileBuilder):
    _type = 'Report'
    _template = 'report.html'

    def _format_data(self):
        self._context.data_model.remove_errors()
        self._context.data_model.remove_keywords()

    def _get_settings(self):
        return {
            'title': self._settings['ReportTitle'],
            'background' : self._resolve_background_colors(),
            'logURL': self._url_from_path(self._path,
                                          self._parse_file('Log'))
        }

    def _resolve_background_colors(self):
        colors = self._settings['ReportBackground']
        return {'pass': colors[0], 'nonCriticalFail': colors[1], 'fail': colors[2]}

class HTMLFileWriter(object):
    _js_file_matcher = re.compile('src=\"([^\"]+)\"')
    _css_file_matcher = re.compile('href=\"([^\"]+)\"')
    _css_media_matcher = re.compile('media=\"([^\"]+)\"')

    def __init__(self, outfile, output):
        self._outfile = outfile
        self._output = output

    def line(self, line):
        if self._is_output_js(line):
            self._write_output_js()
        elif self._is_js_line(line):
            self._inline_js_file(line)
        elif self._is_css_line(line):
            self._inline_css_file(line)
        elif self._is_meta_generator_line(line):
            self._write_meta_generator()
        else:
            self._write(line)

    def _is_output_js(self, line):
        return line.startswith('<!-- OUTPUT JS -->')

    def _is_css_line(self, line):
        return line.startswith('<link rel="stylesheet"')

    def _is_js_line(self, line):
        return line.startswith('<script type="text/javascript" src=')

    def _is_meta_generator_line(self, line):
        return line.startswith('<meta name="Generator" content=')

    def _write_meta_generator(self):
        self._write('<meta name="Generator" content="%s">\n'
                    % get_full_version('Robot Framework'))

    def _write_output_js(self):
        separator = '</script>\n<script type="text/javascript">\n'
        self._write_tag('script', 'type="text/javascript"',
                        lambda: self._output.write_to(self._outfile, separator))

    def _inline_js_file(self, line):
        self._write_tag('script', 'type="text/javascript"',
                        lambda: self._inline_file(line, self._js_file_matcher))

    def _inline_css_file(self, line):
        attrs = 'type="text/css" media="%s"' % self._parse_css_media_type(line)
        self._write_tag('style', attrs,
                        lambda: self._inline_file(line, self._css_file_matcher))

    def _parse_css_media_type(self, line):
        return self._css_media_matcher.search(line).group(1)

    def _inline_file(self, line, filename_matcher):
        self._write_file_content(filename_matcher.search(line).group(1))

    def _write(self, content):
        self._outfile.write(content)

    def _write_tag(self, tag_name, attrs, content_writer):
        # TODO: Use utils.HtmlWriter instead. It also eases giving attrs here.
        self._write('<%s %s>\n' % (tag_name, attrs))
        content_writer()
        self._write('</%s>\n\n' % tag_name)

    def _write_file_content(self, source):
        for line in _WebContentFile(source):
            self._write(line)
        self._write('\n')

class _WebContentFile(object):
    _fs_base = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                            '..', 'webcontent')
    _jar_base = '/Lib/robot/webcontent/'

    def __init__(self, filename):
        self._filename = filename

    def __iter__(self):
        if JarReader:
            return self._iterate_file_in_jar()
        return self._iterate_file_in_filesystem()

    def _iterate_file_in_filesystem(self):
        path = os.path.join(self._fs_base, self._filename)
        with codecs.open(path, 'r', encoding='UTF-8') as file:
            for line in file:
                yield line

    def _iterate_file_in_jar(self):
        file = JarReader(self._jar_base + self._filename)
        line = file.readLine()
        while line is not None:
            yield line + '\n'
            line = file.readLine()
<commit_msg>bug .. fix it<commit_after>#  Copyright 2008-2011 Nokia Siemens Networks Oyj
#
#  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 __future__ import with_statement
import os
import os.path
import re
import sys
import tempfile
import codecs

from robot.output import LOGGER
from robot.reporting.jsondatamodel import SeparatingWriter
from robot.version import get_full_version
from robot import utils

try:
    from org.robotframework.RobotRunner import getResourceAsStream
except ImportError:  # Occurs unless using robotframework.jar
    JarReader = None
else:
    from java.io import BufferedReader, InputStreamReader
    def JarReader(path):
        return BufferedReader(InputStreamReader(getResourceAsStream(path)))

class _Builder(object):

    def __init__(self, context):
        self._settings = context.settings
        self._context = context
        self._path = self._parse_file(self._type)

    def build(self):
        raise NotImplementedError(self.__class__.__name__)

    def _parse_file(self, name):
        value = self._settings[name]
        return value if value != 'NONE' else None

class OutputBuilder(_Builder):
    _type = 'Output'
    _temp_file = None

    def build(self):
        if self._path:
            self._context.result_from_xml.serialize_output(self._path)

class XUnitBuilder(_Builder):
    _type = 'XUnitFile'

    def build(self):
        if self._path:
            self._context.result_from_xml.serialize_xunit(self._path)

class _HTMLFileBuilder(_Builder):
    _type = NotImplemented
    _template = NotImplemented

    def build(self):
        if self._path:
            self._context.data_model.set_settings(self._get_settings())
            self._format_data()
            if self._write_file():
                LOGGER.output_file(self._type, self._path)

    def _url_from_path(self, source, destination):
        if not destination:
            return None
        return utils.get_link_path(destination, os.path.dirname(source))

    def _write_file(self):
        try:
            with codecs.open(self._path, 'w', encoding='UTF-8') as outfile:
                writer = HTMLFileWriter(outfile, self._context.data_model)
                for line in _WebContentFile(self._template):
                    writer.line(line)
        except EnvironmentError, err:
            LOGGER.error("Opening '%s' failed: %s"
                         % (err.filename, err.strerror))
            return False
        return True

class LogBuilder(_HTMLFileBuilder):
    _type = 'Log'
    _template = 'log.html'

    def _format_data(self):
        if self._context.data_model._split_results:
            self._write_split_tests()

    def _write_split_tests(self):
        basename = os.path.splitext(self._path)[0]
        for index, (keywords, strings) in enumerate(self._context.data_model._split_results):
            index += 1  # enumerate accepts start index only in Py 2.6+
            self._write_test(index, keywords, strings, '%s-%d.js' % (basename, index))

    def _write_test(self, index, keywords, strings, path):
        # TODO: Refactor heavily - ask Jussi or Peke for more details
        with codecs.open(path, 'w', encoding='UTF-8') as outfile:
            writer = SeparatingWriter(outfile, '')
            writer.dump_json('window.keywords%d = ' % index, keywords)
            writer.dump_json('window.strings%d = ' % index, strings)
            writer.write('window.fileLoading.notify("%s");\n' % os.path.basename(path))

    def _get_settings(self):
        return  {
            'title': self._settings['LogTitle'],
            'reportURL': self._url_from_path(self._path,
                                             self._parse_file('Report')),
            'splitLogBase': os.path.basename(os.path.splitext(self._path)[0])
        }

class ReportBuilder(_HTMLFileBuilder):
    _type = 'Report'
    _template = 'report.html'

    def _format_data(self):
        self._context.data_model.remove_errors()
        self._context.data_model.remove_keywords()

    def _get_settings(self):
        return {
            'title': self._settings['ReportTitle'],
            'background' : self._resolve_background_colors(),
            'logURL': self._url_from_path(self._path,
                                          self._parse_file('Log'))
        }

    def _resolve_background_colors(self):
        colors = self._settings['ReportBackground']
        return {'pass': colors[0], 'nonCriticalFail': colors[1], 'fail': colors[2]}

class HTMLFileWriter(object):
    _js_file_matcher = re.compile('src=\"([^\"]+)\"')
    _css_file_matcher = re.compile('href=\"([^\"]+)\"')
    _css_media_matcher = re.compile('media=\"([^\"]+)\"')

    def __init__(self, outfile, output):
        self._outfile = outfile
        self._output = output

    def line(self, line):
        if self._is_output_js(line):
            self._write_output_js()
        elif self._is_js_line(line):
            self._inline_js_file(line)
        elif self._is_css_line(line):
            self._inline_css_file(line)
        elif self._is_meta_generator_line(line):
            self._write_meta_generator()
        else:
            self._write(line)

    def _is_output_js(self, line):
        return line.startswith('<!-- OUTPUT JS -->')

    def _is_css_line(self, line):
        return line.startswith('<link rel="stylesheet"')

    def _is_js_line(self, line):
        return line.startswith('<script type="text/javascript" src=')

    def _is_meta_generator_line(self, line):
        return line.startswith('<meta name="Generator" content=')

    def _write_meta_generator(self):
        self._write('<meta name="Generator" content="%s">\n'
                    % get_full_version('Robot Framework'))

    def _write_output_js(self):
        separator = '</script>\n<script type="text/javascript">\n'
        self._write_tag('script', 'type="text/javascript"',
                        lambda: self._output.write_to(self._outfile, separator))

    def _inline_js_file(self, line):
        self._write_tag('script', 'type="text/javascript"',
                        lambda: self._inline_file(line, self._js_file_matcher))

    def _inline_css_file(self, line):
        attrs = 'type="text/css" media="%s"' % self._parse_css_media_type(line)
        self._write_tag('style', attrs,
                        lambda: self._inline_file(line, self._css_file_matcher))

    def _parse_css_media_type(self, line):
        return self._css_media_matcher.search(line).group(1)

    def _inline_file(self, line, filename_matcher):
        self._write_file_content(filename_matcher.search(line).group(1))

    def _write(self, content):
        self._outfile.write(content)

    def _write_tag(self, tag_name, attrs, content_writer):
        # TODO: Use utils.HtmlWriter instead. It also eases giving attrs here.
        self._write('<%s %s>\n' % (tag_name, attrs))
        content_writer()
        self._write('</%s>\n\n' % tag_name)

    def _write_file_content(self, source):
        for line in _WebContentFile(source):
            self._write(line)
        self._write('\n')

class _WebContentFile(object):
    _fs_base = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                            '..', 'webcontent')
    _jar_base = '/Lib/robot/webcontent/'

    def __init__(self, filename):
        self._filename = filename

    def __iter__(self):
        if JarReader:
            return self._iterate_file_in_jar()
        return self._iterate_file_in_filesystem()

    def _iterate_file_in_filesystem(self):
        path = os.path.join(self._fs_base, self._filename)
        with codecs.open(path, 'r', encoding='UTF-8') as file:
            for line in file:
                yield line

    def _iterate_file_in_jar(self):
        file = JarReader(self._jar_base + self._filename)
        line = file.readLine()
        while line is not None:
            yield line + '\n'
            line = file.readLine()
<|endoftext|>