<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2009, MARIMORE Inc Tokyo, Japan.
# Contributed by
#       Iqbal Abdullah <iqbal@marimore.co.jp>
# 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 MARIMORE Inc nor the names of its contributors
#       may be used to endorse or promote products derived from this software
#       without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""
This module defines helper functions
"""

__author__      = "Iqbal Abdullah <iqbal@marimore.co.jp>"
__date__        = "$LastChangedDate$"
__version__     = "$LastChangedRevision$"

import sys

def import_object(qualified_name):
    """
    import_object() will return the module/function/class which is specified
    in qualified_name

    @type qualified_name: string
    @param qualified_name: The fully qualified package path to the target
    @rtype: object or None
    @return: An object to the target module/function/class or None if an exception
             occurred
    """

    parent_namespace = ".".join(qualified_name.split(".")[:-1])
    target_namespace = qualified_name.split(".")[-1]

    if parent_namespace == "":
        # This is the top module
        parent_namespace = target_namespace

    try:
        __import__(parent_namespace)
        m = sys.modules[parent_namespace]
        mod = getattr(m, target_namespace)
        return mod

    except (Exception) as e:
        print "Exception occurred 02: import_object(): %s" % (e)
        return None

def log_syslog(ident, message, priority="LOG_NOTICE", facility="LOG_USER"):
    """
    Writes log messages via the system syslog(). This is for UNIX based systems only.

    @type facility: string
    @param facility: The facility to be used. It can be one of these:
        1. LOG_KERN
        2. LOG_USER
        3. LOG_MAIL
        4. LOG_DAEMON
        5. LOG_AUTH
        6. LOG_LPR
        7. LOG_NEWS
        8. LOG_UUCP
        9. LOG_CRON
        10. LOG_LOCAL0 to LOG_LOCAL7
    By default it will be LOG_USER

    @type priority: string
    @param priority: The priority of the message, which can be one of these:
        1. LOG_EMERG
        2. LOG_ALERT
        3. LOG_CRIT
        4. LOG_ERR
        5. LOG_WARNING
        6. LOG_NOTICE
        7. LOG_INFO
        8. LOG_DEBUG
    By default it is LOG_NOTICE

    @type ident: string
    @param ident: The prepended indent for your message. Usually this is the filename.
    @type message: string
    @param message: The message you want to log to
    """

    try:
        import syslog
    except:
        return -1

    if facility == "LOG_KERN":
        fac = syslog.LOG_KERN
    elif facility == "LOG_MAIL":
        fac = syslog.LOG_MAIL
    elif facility == "LOG_DAEMON":
        fac = syslog.LOG_DAEMON
    elif facility == "LOG_AUTH":
        fac = syslog.LOG_AUTH
    elif facility == "LOG_LPR":
        fac = syslog.LOG_LPR
    elif facility == "LOG_NEWS":
        fac = syslog.LOG_NEWS
    elif facility == "LOG_UUCP":
        fac = syslog.LOG_UUCP
    elif facility == "LOG_CRON":
        fac = syslog.LOG_CRON
    elif facility[:9] == "LOG_LOCAL":
        if facility[9] == "0":
            fac = syslog.LOG_LOCAL0
        elif facility[9] == "1":
            fac = syslog.LOG_LOCAL1
        elif facility[9] == "2":
            fac = syslog.LOG_LOCAL2
        elif facility[9] == "3":
            fac = syslog.LOG_LOCAL3
        elif facility[9] == "4":
            fac = syslog.LOG_LOCAL4
        elif facility[9] == "5":
            fac = syslog.LOG_LOCAL5
        elif facility[9] == "6":
            fac = syslog.LOG_LOCAL6
        elif facility[9] == "7":
            fac = syslog.LOG_LOCAL7
        else:
            # There's only 0 to 7
            fac = syslog.LOG_USER
    else:
        fac = syslog.LOG_USER

    if priority == "LOG_EMERG":
        prio = syslog.LOG_EMERG
    elif priority == "LOG_ALERT":
        prio = syslog.LOG_ALERT
    elif priority == "LOG_CRIT":
        prio = syslog.LOG_CRIT
    elif priority == "LOG_ERR":
        prio = syslog.LOG_ERR
    elif priority == "LOG_WARNING":
        prio = syslog.LOG_WARNING
    elif priority == "LOG_INFO":
        prio = syslog.LOG_INFO
    elif priority == "LOG_DEBUG":
        prio = syslog.LOG_DEBUG
    else:
        prio = syslog.LOG_NOTICE

    syslog.openlog(ident, 0, fac)
    syslog.syslog(prio, message)

def uniqify_list(seq, idfun=None):
    """
    Uniqify a list while preserving order. i.e

    If you have a list such as [1,2,2,2,3,4,5,6,6,6,6] uniqify_list() while
    return [1,2,3,4,5,6]

    You can also specify a function to transform the data, i.e

    >>> a=list('ABeeE')
    >>> uniqify_list(a)
    ['A','B','e','E']
    >>> uniqify_list(a, lambda x: x.lower())
    ['A','B','e']

    Taken from http://www.peterbe.com/plog/uniqifiers-benchmark

    @type seq: list
    @param seq: The list which you want to uniqify
    @type idfun: a function, defaults to None
    @param idfun: A function which you want to process the data with
    @rtype: list
    @return: An list which has been uniqified
    """

    if idfun is None:
        def idfun(x): return x

    seen = {}
    result = []
    for item in seq:
        marker = idfun(item)
        if marker in seen: continue
        seen[marker] = 1
        result.append(item)

    return result

if __name__ == '__main__':
    log_syslog("test", "test test")
    #import_object('test')
    print uniqify_list(list('123123454324332ABCAABCabcaabcrt'))
<commit_msg>Added calculate_time()<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2009, MARIMORE Inc Tokyo, Japan.
# Contributed by
#       Iqbal Abdullah <iqbal@marimore.co.jp>
# 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 MARIMORE Inc nor the names of its contributors
#       may be used to endorse or promote products derived from this software
#       without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""
This module defines helper functions
"""

__author__      = "Iqbal Abdullah <iqbal@marimore.co.jp>"
__date__        = "$LastChangedDate$"
__version__     = "$LastChangedRevision$"

import sys, datetime

def import_object(qualified_name):
    """
    import_object() will return the module/function/class which is specified
    in qualified_name

    @type qualified_name: string
    @param qualified_name: The fully qualified package path to the target
    @rtype: object or None
    @return: An object to the target module/function/class or None if an exception
             occurred
    """

    parent_namespace = ".".join(qualified_name.split(".")[:-1])
    target_namespace = qualified_name.split(".")[-1]

    if parent_namespace == "":
        # This is the top module
        parent_namespace = target_namespace

    try:
        __import__(parent_namespace)
        m = sys.modules[parent_namespace]
        mod = getattr(m, target_namespace)
        return mod

    except (Exception) as e:
        print "Exception occurred 02: import_object(): %s" % (e)
        return None

def log_syslog(ident, message, priority="LOG_NOTICE", facility="LOG_USER"):
    """
    Writes log messages via the system syslog(). This is for UNIX based systems only.

    @type facility: string
    @param facility: The facility to be used. It can be one of these:
        1. LOG_KERN
        2. LOG_USER
        3. LOG_MAIL
        4. LOG_DAEMON
        5. LOG_AUTH
        6. LOG_LPR
        7. LOG_NEWS
        8. LOG_UUCP
        9. LOG_CRON
        10. LOG_LOCAL0 to LOG_LOCAL7
    By default it will be LOG_USER

    @type priority: string
    @param priority: The priority of the message, which can be one of these:
        1. LOG_EMERG
        2. LOG_ALERT
        3. LOG_CRIT
        4. LOG_ERR
        5. LOG_WARNING
        6. LOG_NOTICE
        7. LOG_INFO
        8. LOG_DEBUG
    By default it is LOG_NOTICE

    @type ident: string
    @param ident: The prepended indent for your message. Usually this is the filename.
    @type message: string
    @param message: The message you want to log to
    """

    try:
        import syslog
    except:
        return -1

    if facility == "LOG_KERN":
        fac = syslog.LOG_KERN
    elif facility == "LOG_MAIL":
        fac = syslog.LOG_MAIL
    elif facility == "LOG_DAEMON":
        fac = syslog.LOG_DAEMON
    elif facility == "LOG_AUTH":
        fac = syslog.LOG_AUTH
    elif facility == "LOG_LPR":
        fac = syslog.LOG_LPR
    elif facility == "LOG_NEWS":
        fac = syslog.LOG_NEWS
    elif facility == "LOG_UUCP":
        fac = syslog.LOG_UUCP
    elif facility == "LOG_CRON":
        fac = syslog.LOG_CRON
    elif facility[:9] == "LOG_LOCAL":
        if facility[9] == "0":
            fac = syslog.LOG_LOCAL0
        elif facility[9] == "1":
            fac = syslog.LOG_LOCAL1
        elif facility[9] == "2":
            fac = syslog.LOG_LOCAL2
        elif facility[9] == "3":
            fac = syslog.LOG_LOCAL3
        elif facility[9] == "4":
            fac = syslog.LOG_LOCAL4
        elif facility[9] == "5":
            fac = syslog.LOG_LOCAL5
        elif facility[9] == "6":
            fac = syslog.LOG_LOCAL6
        elif facility[9] == "7":
            fac = syslog.LOG_LOCAL7
        else:
            # There's only 0 to 7
            fac = syslog.LOG_USER
    else:
        fac = syslog.LOG_USER

    if priority == "LOG_EMERG":
        prio = syslog.LOG_EMERG
    elif priority == "LOG_ALERT":
        prio = syslog.LOG_ALERT
    elif priority == "LOG_CRIT":
        prio = syslog.LOG_CRIT
    elif priority == "LOG_ERR":
        prio = syslog.LOG_ERR
    elif priority == "LOG_WARNING":
        prio = syslog.LOG_WARNING
    elif priority == "LOG_INFO":
        prio = syslog.LOG_INFO
    elif priority == "LOG_DEBUG":
        prio = syslog.LOG_DEBUG
    else:
        prio = syslog.LOG_NOTICE

    syslog.openlog(ident, 0, fac)
    syslog.syslog(prio, message)

def uniqify_list(seq, idfun=None):
    """
    Uniqify a list while preserving order. i.e

    If you have a list such as [1,2,2,2,3,4,5,6,6,6,6] uniqify_list() while
    return [1,2,3,4,5,6]

    You can also specify a function to transform the data, i.e

    >>> a=list('ABeeE')
    >>> uniqify_list(a)
    ['A','B','e','E']
    >>> uniqify_list(a, lambda x: x.lower())
    ['A','B','e']

    Taken from http://www.peterbe.com/plog/uniqifiers-benchmark

    @type seq: list
    @param seq: The list which you want to uniqify
    @type idfun: a function, defaults to None
    @param idfun: A function which you want to process the data with
    @rtype: list
    @return: An list which has been uniqified
    """

    if idfun is None:
        def idfun(x): return x

    seen = {}
    result = []
    for item in seq:
        marker = idfun(item)
        if marker in seen: continue
        seen[marker] = 1
        result.append(item)

    return result

def calculate_time(base_time, diff_seconds):
    """
    Calculates a new time if you add diff_seconds to base_time

    @type base_time: datetime object
    @param base_time: The base time which you want to do the calculation on
    @type diff_seconds: signed int
    @param diff_seconds: The time difference in seconds. Use a negative value if the time is in the past
    @rtype: datetime object
    @return: A datetime object representing the calculated time
    """

    diff_time = datetime.timedelta(seconds=diff_seconds)
    new_time = base_time + diff_time
    return new_time

if __name__ == '__main__':
    #log_syslog("test", "test test")
    #import_object('test')
    print uniqify_list(list('123123454324332ABCAABCabcaabcrt'))
    print calculate_time(datetime.datetime.now(), -60)
<|endoftext|>


<commit_before>import sys
from os import sep, path as p
from fabric.api import *
from fabric.contrib.files import exists
from fabric.contrib.project import rsync_project
from fabfile.util import slashed, cygpath
from fabfile.target import _needs_targetenv
from fabfile.util import venv, fullpath

def get_build_dir():
    return sep.join((env.builddir, env.target, 'rinfo-admin'))

@task
@runs_once
@roles('admin')
def setup():
    _needs_targetenv()
    if not exists(env.admin_webroot):
       sudo("mkdir %(admin_webroot)s" % env)
       sudo("chown %(user)s %(admin_webroot)s" % env)

@task
def package(source=None):
    """
    Package the admin feed files into a servable directory.
    """
    source = source or env.target
    tg_sources = p.join(env.projectroot, "resources", source, "datasources.n3")
    sourceopt = "-s " + fullpath(tg_sources) if p.exists(tg_sources) else ""
    outdiropt ="-o " + fullpath(get_build_dir())
    local("cd %(toolsdir)s/rinfomain && groovy base_as_feed.groovy "
            " -b %(baseresources)s %(sourceopt)s %(outdiropt)s" % venv())

@task
@roles('admin')
def deploy():
    """Deploy the admin feed to target env."""
    setup()
    builddir = get_build_dir()
    if sys.platform == 'win32':
        # Support cygwin rsync on windows:
        build_path = cygpath(slashed(builddir))
    else:
        build_path = slashed(builddir)
    rsync_project((env.admin_webroot), build_path, exclude=".*", delete=True)

@task
@roles('admin')
def all(source=None):
    """Package and deploy the admin feed."""
    package(source)
    deploy()

@task
@roles('admin')
def test():
    """Http request to test admin is up and running correctly"""
    admin_url = "http://%s/" % env.roledefs['admin'][0]
    print local("curl %(admin_url)s"%vars())
    # Should test the response to validate the admin servers correctness

@task
@roles('admin')
def clean():
    """Completetly remove all admin contents from server"""
    sudo("rm -rf %(admin_webroot)s" % env)

@task
def ping_main():
    """Ping rinfo-main to (re-)collect the admin feed"""
    _needs_targetenv()
    feed_url = "http://%s/feed/current" % env.roledefs['admin'][0]
    collector_url = "http://%s/collector" % env.roledefs['main'][0]
    print local("curl --data 'feed=%(feed_url)s' %(collector_url)s"%vars())

<commit_msg>Barbone implementation of regression admin test<commit_after>import sys
from os import sep, path as p
from fabric.api import *
from fabric.contrib.files import exists
from fabric.contrib.project import rsync_project
from fabfile.util import slashed, cygpath
from fabfile.target import _needs_targetenv
from fabfile.util import venv, fullpath

def get_build_dir():
    return sep.join((env.builddir, env.target, 'rinfo-admin'))

@task
@runs_once
@roles('admin')
def setup():
    _needs_targetenv()
    if not exists(env.admin_webroot):
       sudo("mkdir %(admin_webroot)s" % env)
       sudo("chown %(user)s %(admin_webroot)s" % env)

@task
def package(source=None):
    """
    Package the admin feed files into a servable directory.
    """
    source = source or env.target
    tg_sources = p.join(env.projectroot, "resources", source, "datasources.n3")
    sourceopt = "-s " + fullpath(tg_sources) if p.exists(tg_sources) else ""
    outdiropt ="-o " + fullpath(get_build_dir())
    local("cd %(toolsdir)s/rinfomain && groovy base_as_feed.groovy "
            " -b %(baseresources)s %(sourceopt)s %(outdiropt)s" % venv())

@task
@roles('admin')
def deploy():
    """Deploy the admin feed to target env."""
    setup()
    builddir = get_build_dir()
    if sys.platform == 'win32':
        # Support cygwin rsync on windows:
        build_path = cygpath(slashed(builddir))
    else:
        build_path = slashed(builddir)
    rsync_project((env.admin_webroot), build_path, exclude=".*", delete=True)

@task
@roles('admin')
def all(source=None):
    """Package and deploy the admin feed."""
    package(source)
    deploy()

@task
@roles('admin')
def test():
    """Http request to test admin is up and running correctly"""
    admin_url = "http://%s/" % env.roledefs['admin'][0]
    respHttp = local("curl %(admin_url)s"%vars())
    print respHttp
    if not "bla bla bla" in respHttp:
        print "Fail!!!!"
    # Should test the response to validate the admin servers correctness

@task
@roles('admin')
def clean():
    """Completetly remove all admin contents from server"""
    sudo("rm -rf %(admin_webroot)s" % env)

@task
def ping_main():
    """Ping rinfo-main to (re-)collect the admin feed"""
    _needs_targetenv()
    feed_url = "http://%s/feed/current" % env.roledefs['admin'][0]
    collector_url = "http://%s/collector" % env.roledefs['main'][0]
    print local("curl --data 'feed=%(feed_url)s' %(collector_url)s"%vars())

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-TODAY OpenERP S.A. <http://www.openerp.com>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import logging
import threading
import time
import psycopg2
from datetime import datetime
from dateutil.relativedelta import relativedelta

import openerp
from openerp import netsvc
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _

_logger = logging.getLogger(__name__)

def str2tuple(s):
    return eval('tuple(%s)' % (s or ''))

_intervalTypes = {
    'work_days': lambda interval: relativedelta(days=interval),
    'days': lambda interval: relativedelta(days=interval),
    'hours': lambda interval: relativedelta(hours=interval),
    'weeks': lambda interval: relativedelta(days=7*interval),
    'months': lambda interval: relativedelta(months=interval),
    'minutes': lambda interval: relativedelta(minutes=interval),
}

class ir_cron(osv.osv):
    """ Model describing cron jobs (also called actions or tasks).
    """

    # TODO: perhaps in the future we could consider a flag on ir.cron jobs
    # that would cause database wake-up even if the database has not been
    # loaded yet or was already unloaded (e.g. 'force_db_wakeup' or something)
    # See also openerp.cron

    _name = "ir.cron"
    _order = 'name'
    _columns = {
        'name': fields.char('Name', size=60, required=True),
        'user_id': fields.many2one('res.users', 'User', required=True),
        'active': fields.boolean('Active'),
        'interval_number': fields.integer('Interval Number',help="Repeat every x."),
        'interval_type': fields.selection( [('minutes', 'Minutes'),
            ('hours', 'Hours'), ('work_days','Work Days'), ('days', 'Days'),('weeks', 'Weeks'), ('months', 'Months')], 'Interval Unit'),
        'numbercall': fields.integer('Number of Calls', help='How many times the method is called,\na negative number indicates no limit.'),
        'doall' : fields.boolean('Repeat Missed', help="Specify if missed occurrences should be executed when the server restarts."),
        'nextcall' : fields.datetime('Next Execution Date', required=True, help="Next planned execution date for this job."),
        'model': fields.char('Object', size=64, help="Model name on which the method to be called is located, e.g. 'res.partner'."),
        'function': fields.char('Method', size=64, help="Name of the method to be called when this job is processed."),
        'args': fields.text('Arguments', help="Arguments to be passed to the method, e.g. (uid,)."),
        'priority': fields.integer('Priority', help='The priority of the job, as an integer: 0 means higher priority, 10 means lower priority.')
    }

    _defaults = {
        'nextcall' : lambda *a: time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
        'priority' : 5,
        'user_id' : lambda obj,cr,uid,context: uid,
        'interval_number' : 1,
        'interval_type' : 'months',
        'numbercall' : 1,
        'active' : 1,
        'doall' : 1
    }

    def _check_args(self, cr, uid, ids, context=None):
        try:
            for this in self.browse(cr, uid, ids, context):
                str2tuple(this.args)
        except Exception:
            return False
        return True

    _constraints = [
        (_check_args, 'Invalid arguments', ['args']),
    ]

    def _handle_callback_exception(self, cr, uid, model_name, method_name, args, job_id, job_exception):
        """ Method called when an exception is raised by a job.

        Simply logs the exception and rollback the transaction.

        :param model_name: model name on which the job method is located.
        :param method_name: name of the method to call when this job is processed.
        :param args: arguments of the method (without the usual self, cr, uid).
        :param job_id: job id.
        :param job_exception: exception raised by the job.

        """
        cr.rollback()
        _logger.exception("Call of self.pool.get('%s').%s(cr, uid, *%r) failed in Job %s" % (model_name, method_name, args, job_id))

    def _callback(self, cr, uid, model_name, method_name, args, job_id):
        """ Run the method associated to a given job

        It takes care of logging and exception handling.

        :param model_name: model name on which the job method is located.
        :param method_name: name of the method to call when this job is processed.
        :param args: arguments of the method (without the usual self, cr, uid).
        :param job_id: job id.
        """
        args = str2tuple(args)
        model = self.pool.get(model_name)
        if model and hasattr(model, method_name):
            method = getattr(model, method_name)
            try:
                log_depth = (None if _logger.isEnabledFor(logging.DEBUG) else 1)
                netsvc.log(_logger, logging.DEBUG, 'cron.object.execute', (cr.dbname,uid,'*',model_name,method_name)+tuple(args), depth=log_depth)
                if _logger.isEnabledFor(logging.DEBUG):
                    start_time = time.time()
                method(cr, uid, *args)
                if _logger.isEnabledFor(logging.DEBUG):
                    end_time = time.time()
                    _logger.debug('%.3fs (%s, %s)' % (end_time - start_time, model_name, method_name))
            except Exception, e:
                self._handle_callback_exception(cr, uid, model_name, method_name, args, job_id, e)

    def _process_job(self, job_cr, job, cron_cr):
        """ Run a given job taking care of the repetition.

        :param job_cr: cursor to use to execute the job, safe to commit/rollback
        :param job: job to be run (as a dictionary).
        :param cron_cr: cursor holding lock on the cron job row, to use to update the next exec date,
            must not be committed/rolled back!
        """
        try:
            now = datetime.now()
            nextcall = datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT)
            numbercall = job['numbercall']

            ok = False
            while nextcall < now and numbercall:
                if numbercall > 0:
                    numbercall -= 1
                if not ok or job['doall']:
                    self._callback(job_cr, job['user_id'], job['model'], job['function'], job['args'], job['id'])
                if numbercall:
                    nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
                ok = True
            addsql = ''
            if not numbercall:
                addsql = ', active=False'
            cron_cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s",
                       (nextcall.strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id']))

        finally:
            job_cr.commit()
            cron_cr.commit()

    @classmethod
    def _acquire_job(cls, db_name):
        # TODO remove 'check' argument from addons/base_action_rule/base_action_rule.py
        """ Try to process one cron job.

        This selects in database all the jobs that should be processed. It then
        tries to lock each of them and, if it succeeds, run the cron job (if it
        doesn't succeed, it means the job was already locked to be taken care
        of by another thread) and return.

        If a job was processed, returns True, otherwise returns False.
        """
        db = openerp.sql_db.db_connect(db_name)
        threading.current_thread().dbname = db_name
        cr = db.cursor()
        jobs = []
        try:
            # Careful to compare timestamps with 'UTC' - everything is UTC as of v6.1.
            cr.execute("""SELECT * FROM ir_cron
                          WHERE numbercall != 0
                              AND active AND nextcall <= (now() at time zone 'UTC')
                          ORDER BY priority""")
            jobs = cr.dictfetchall()
        except psycopg2.ProgrammingError, e:
            if e.pgcode == '42P01':
                # Class 42 — Syntax Error or Access Rule Violation; 42P01: undefined_table
                # The table ir_cron does not exist; this is probably not an OpenERP database.
                _logger.warning('Tried to poll an undefined table on database %s.', db_name)
            else:
                raise
        except Exception:
            _logger.warning('Exception in cron:', exc_info=True)
        finally:
            cr.close()

        for job in jobs:
            lock_cr = db.cursor()
            try:
                # Try to grab an exclusive lock on the job row from within the task transaction
                lock_cr.execute("""SELECT *
                                   FROM ir_cron
                                   WHERE id=%s
                                   FOR UPDATE NOWAIT""",
                               (job['id'],), log_exceptions=False)

                # Got the lock on the job row, run its code
                _logger.debug('Starting job `%s`.', job['name'])
                job_cr = db.cursor()
                try:
                    openerp.modules.registry.RegistryManager.check_registry_signaling(db_name)
                    registry = openerp.pooler.get_pool(db_name)
                    registry[cls._name]._process_job(job_cr, job, lock_cr)
                    openerp.modules.registry.RegistryManager.signal_caches_change(db_name)
                except Exception:
                    _logger.exception('Unexpected exception while processing cron job %r', job)
                finally:
                    job_cr.close()

            except psycopg2.OperationalError, e:
                if e.pgcode == '55P03':
                    # Class 55: Object not in prerequisite state; 55P03: lock_not_available
                    _logger.debug('Another process/thread is already busy executing job `%s`, skipping it.', job['name'])
                    continue
                else:
                    # Unexpected OperationalError
                    raise
            finally:
                # we're exiting due to an exception while acquiring the lock
                lock_cr.close()

        if hasattr(threading.current_thread(), 'dbname'): # cron job could have removed it as side-effect
            del threading.current_thread().dbname

    def _try_lock(self, cr, uid, ids, context=None):
        """Try to grab a dummy exclusive write-lock to the rows with the given ids,
           to make sure a following write() or unlink() will not block due
           to a process currently executing those cron tasks"""
        try:
            cr.execute("""SELECT id FROM "%s" WHERE id IN %%s FOR UPDATE NOWAIT""" % self._table,
                       (tuple(ids),), log_exceptions=False)
        except psycopg2.OperationalError:
            cr.rollback() # early rollback to allow translations to work for the user feedback
            raise osv.except_osv(_("Record cannot be modified right now"),
                                 _("This cron task is currently being executed and may not be modified, "
                                  "please try again in a few minutes"))

    def create(self, cr, uid, vals, context=None):
        res = super(ir_cron, self).create(cr, uid, vals, context=context)
        return res

    def write(self, cr, uid, ids, vals, context=None):
        self._try_lock(cr, uid, ids, context)
        res = super(ir_cron, self).write(cr, uid, ids, vals, context=context)
        return res

    def unlink(self, cr, uid, ids, context=None):
        self._try_lock(cr, uid, ids, context)
        res = super(ir_cron, self).unlink(cr, uid, ids, context=context)
        return res

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>[FIX] ir.cron add a safeguard mecanism against version mistach when polling<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-TODAY OpenERP S.A. <http://www.openerp.com>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import logging
import threading
import time
import psycopg2
from datetime import datetime
from dateutil.relativedelta import relativedelta

import openerp
from openerp import netsvc
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
from openerp.modules import load_information_from_description_file

_logger = logging.getLogger(__name__)

BASE_VERSION = load_information_from_description_file('base')['version']

def str2tuple(s):
    return eval('tuple(%s)' % (s or ''))

_intervalTypes = {
    'work_days': lambda interval: relativedelta(days=interval),
    'days': lambda interval: relativedelta(days=interval),
    'hours': lambda interval: relativedelta(hours=interval),
    'weeks': lambda interval: relativedelta(days=7*interval),
    'months': lambda interval: relativedelta(months=interval),
    'minutes': lambda interval: relativedelta(minutes=interval),
}

class ir_cron(osv.osv):
    """ Model describing cron jobs (also called actions or tasks).
    """

    # TODO: perhaps in the future we could consider a flag on ir.cron jobs
    # that would cause database wake-up even if the database has not been
    # loaded yet or was already unloaded (e.g. 'force_db_wakeup' or something)
    # See also openerp.cron

    _name = "ir.cron"
    _order = 'name'
    _columns = {
        'name': fields.char('Name', size=60, required=True),
        'user_id': fields.many2one('res.users', 'User', required=True),
        'active': fields.boolean('Active'),
        'interval_number': fields.integer('Interval Number',help="Repeat every x."),
        'interval_type': fields.selection( [('minutes', 'Minutes'),
            ('hours', 'Hours'), ('work_days','Work Days'), ('days', 'Days'),('weeks', 'Weeks'), ('months', 'Months')], 'Interval Unit'),
        'numbercall': fields.integer('Number of Calls', help='How many times the method is called,\na negative number indicates no limit.'),
        'doall' : fields.boolean('Repeat Missed', help="Specify if missed occurrences should be executed when the server restarts."),
        'nextcall' : fields.datetime('Next Execution Date', required=True, help="Next planned execution date for this job."),
        'model': fields.char('Object', size=64, help="Model name on which the method to be called is located, e.g. 'res.partner'."),
        'function': fields.char('Method', size=64, help="Name of the method to be called when this job is processed."),
        'args': fields.text('Arguments', help="Arguments to be passed to the method, e.g. (uid,)."),
        'priority': fields.integer('Priority', help='The priority of the job, as an integer: 0 means higher priority, 10 means lower priority.')
    }

    _defaults = {
        'nextcall' : lambda *a: time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
        'priority' : 5,
        'user_id' : lambda obj,cr,uid,context: uid,
        'interval_number' : 1,
        'interval_type' : 'months',
        'numbercall' : 1,
        'active' : 1,
        'doall' : 1
    }

    def _check_args(self, cr, uid, ids, context=None):
        try:
            for this in self.browse(cr, uid, ids, context):
                str2tuple(this.args)
        except Exception:
            return False
        return True

    _constraints = [
        (_check_args, 'Invalid arguments', ['args']),
    ]

    def _handle_callback_exception(self, cr, uid, model_name, method_name, args, job_id, job_exception):
        """ Method called when an exception is raised by a job.

        Simply logs the exception and rollback the transaction.

        :param model_name: model name on which the job method is located.
        :param method_name: name of the method to call when this job is processed.
        :param args: arguments of the method (without the usual self, cr, uid).
        :param job_id: job id.
        :param job_exception: exception raised by the job.

        """
        cr.rollback()
        _logger.exception("Call of self.pool.get('%s').%s(cr, uid, *%r) failed in Job %s" % (model_name, method_name, args, job_id))

    def _callback(self, cr, uid, model_name, method_name, args, job_id):
        """ Run the method associated to a given job

        It takes care of logging and exception handling.

        :param model_name: model name on which the job method is located.
        :param method_name: name of the method to call when this job is processed.
        :param args: arguments of the method (without the usual self, cr, uid).
        :param job_id: job id.
        """
        args = str2tuple(args)
        model = self.pool.get(model_name)
        if model and hasattr(model, method_name):
            method = getattr(model, method_name)
            try:
                log_depth = (None if _logger.isEnabledFor(logging.DEBUG) else 1)
                netsvc.log(_logger, logging.DEBUG, 'cron.object.execute', (cr.dbname,uid,'*',model_name,method_name)+tuple(args), depth=log_depth)
                if _logger.isEnabledFor(logging.DEBUG):
                    start_time = time.time()
                method(cr, uid, *args)
                if _logger.isEnabledFor(logging.DEBUG):
                    end_time = time.time()
                    _logger.debug('%.3fs (%s, %s)' % (end_time - start_time, model_name, method_name))
            except Exception, e:
                self._handle_callback_exception(cr, uid, model_name, method_name, args, job_id, e)

    def _process_job(self, job_cr, job, cron_cr):
        """ Run a given job taking care of the repetition.

        :param job_cr: cursor to use to execute the job, safe to commit/rollback
        :param job: job to be run (as a dictionary).
        :param cron_cr: cursor holding lock on the cron job row, to use to update the next exec date,
            must not be committed/rolled back!
        """
        try:
            now = datetime.now()
            nextcall = datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT)
            numbercall = job['numbercall']

            ok = False
            while nextcall < now and numbercall:
                if numbercall > 0:
                    numbercall -= 1
                if not ok or job['doall']:
                    self._callback(job_cr, job['user_id'], job['model'], job['function'], job['args'], job['id'])
                if numbercall:
                    nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
                ok = True
            addsql = ''
            if not numbercall:
                addsql = ', active=False'
            cron_cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s",
                       (nextcall.strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id']))

        finally:
            job_cr.commit()
            cron_cr.commit()

    @classmethod
    def _acquire_job(cls, db_name):
        # TODO remove 'check' argument from addons/base_action_rule/base_action_rule.py
        """ Try to process one cron job.

        This selects in database all the jobs that should be processed. It then
        tries to lock each of them and, if it succeeds, run the cron job (if it
        doesn't succeed, it means the job was already locked to be taken care
        of by another thread) and return.

        If a job was processed, returns True, otherwise returns False.
        """
        db = openerp.sql_db.db_connect(db_name)
        threading.current_thread().dbname = db_name
        cr = db.cursor()
        jobs = []
        try:
            # Make sure the database we poll has the same version as the code of base
            cr.execute("SELECT 1 FROM ir_module_module WHERE name=%s AND latest_version=%s", ('base', BASE_VERSION))
            if cr.fetchone():
                # Careful to compare timestamps with 'UTC' - everything is UTC as of v6.1.
                cr.execute("""SELECT * FROM ir_cron
                              WHERE numbercall != 0
                                  AND active AND nextcall <= (now() at time zone 'UTC')
                              ORDER BY priority""")
                jobs = cr.dictfetchall()
            else:
                _logger.warning('Skipping database %s as its base version is not %s.', db_name, BASE_VERSION)
        except psycopg2.ProgrammingError, e:
            if e.pgcode == '42P01':
                # Class 42 — Syntax Error or Access Rule Violation; 42P01: undefined_table
                # The table ir_cron does not exist; this is probably not an OpenERP database.
                _logger.warning('Tried to poll an undefined table on database %s.', db_name)
            else:
                raise
        except Exception:
            _logger.warning('Exception in cron:', exc_info=True)
        finally:
            cr.close()

        for job in jobs:
            lock_cr = db.cursor()
            try:
                # Try to grab an exclusive lock on the job row from within the task transaction
                lock_cr.execute("""SELECT *
                                   FROM ir_cron
                                   WHERE id=%s
                                   FOR UPDATE NOWAIT""",
                               (job['id'],), log_exceptions=False)

                # Got the lock on the job row, run its code
                _logger.debug('Starting job `%s`.', job['name'])
                job_cr = db.cursor()
                try:
                    openerp.modules.registry.RegistryManager.check_registry_signaling(db_name)
                    registry = openerp.pooler.get_pool(db_name)
                    registry[cls._name]._process_job(job_cr, job, lock_cr)
                    openerp.modules.registry.RegistryManager.signal_caches_change(db_name)
                except Exception:
                    _logger.exception('Unexpected exception while processing cron job %r', job)
                finally:
                    job_cr.close()

            except psycopg2.OperationalError, e:
                if e.pgcode == '55P03':
                    # Class 55: Object not in prerequisite state; 55P03: lock_not_available
                    _logger.debug('Another process/thread is already busy executing job `%s`, skipping it.', job['name'])
                    continue
                else:
                    # Unexpected OperationalError
                    raise
            finally:
                # we're exiting due to an exception while acquiring the lock
                lock_cr.close()

        if hasattr(threading.current_thread(), 'dbname'): # cron job could have removed it as side-effect
            del threading.current_thread().dbname

    def _try_lock(self, cr, uid, ids, context=None):
        """Try to grab a dummy exclusive write-lock to the rows with the given ids,
           to make sure a following write() or unlink() will not block due
           to a process currently executing those cron tasks"""
        try:
            cr.execute("""SELECT id FROM "%s" WHERE id IN %%s FOR UPDATE NOWAIT""" % self._table,
                       (tuple(ids),), log_exceptions=False)
        except psycopg2.OperationalError:
            cr.rollback() # early rollback to allow translations to work for the user feedback
            raise osv.except_osv(_("Record cannot be modified right now"),
                                 _("This cron task is currently being executed and may not be modified, "
                                  "please try again in a few minutes"))

    def create(self, cr, uid, vals, context=None):
        res = super(ir_cron, self).create(cr, uid, vals, context=context)
        return res

    def write(self, cr, uid, ids, vals, context=None):
        self._try_lock(cr, uid, ids, context)
        res = super(ir_cron, self).write(cr, uid, ids, vals, context=context)
        return res

    def unlink(self, cr, uid, ids, context=None):
        self._try_lock(cr, uid, ids, context)
        res = super(ir_cron, self).unlink(cr, uid, ids, context=context)
        return res

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<|endoftext|>


<commit_before>from openpnm.core import Subdomain, ModelsMixin
from openpnm.utils import Workspace, logging
logger = logging.getLogger(__name__)
ws = Workspace()

class GenericPhysics(Subdomain, ModelsMixin):
    r"""
    This generic class is meant as a starter for custom Physics objects

    It produces a blank object with no pore-scale models attached.  Users can
    add models from the ``models`` module (or create their own).

    Parameters
    ----------
    network : OpenPNM Network object
        The network to which this Physics should be attached

    phase : OpenPNM Phase object
        The Phase object to which this Physics applies

    geometry : OpenPNM Geometry object
        The Geometry object that defines the pores/throats where this Physics
        should be applied.

    name : str, optional
        A unique string name to identify the Physics object, typically same as
        instance name but can be anything.  If left blank, and name will be
        generated that include the class name and a random string.

    """

    def __init__(self, project=None, network=None, phase=None,
                 geometry=None, settings={}, **kwargs):

        # Define some default settings
        self.settings.update({'prefix': 'phys'})
        # Overwrite with user supplied settings, if any
        self.settings.update(settings)

        # Deal with network or project arguments
        if network is not None:
            if project is not None:
                assert network is project.network
            else:
                project = network.project

        super().__init__(project=project, **kwargs)

        network = self.project.network
        if network:
            if phase is None:
                raise Exception('All Physics objects must be associated ' +
                                'with a Phase')
            else:
                phase['pore.'+self.name] = False
                phase['throat.'+self.name] = False
            if geometry is None:
                logger.info('No Geometry given, assigning ' + self.name +
                            ' to all pores and throats')
                Ps = network.Ps
                Ts = network.Ts
            else:
                Ps = network.pores(geometry.name)
                Ts = network.throats(geometry.name)
            self._add_locations(pores=Ps, throats=Ts)
<commit_msg>Improving error checking when multiple geoms found<commit_after>from openpnm.core import Subdomain, ModelsMixin
from openpnm.utils import Workspace, logging
logger = logging.getLogger(__name__)
ws = Workspace()

class GenericPhysics(Subdomain, ModelsMixin):
    r"""
    This generic class is meant as a starter for custom Physics objects

    It produces a blank object with no pore-scale models attached.  Users can
    add models from the ``models`` module (or create their own).

    Parameters
    ----------
    network : OpenPNM Network object
        The network to which this Physics should be attached

    phase : OpenPNM Phase object
        The Phase object to which this Physics applies

    geometry : OpenPNM Geometry object
        The Geometry object that defines the pores/throats where this Physics
        should be applied.

    name : str, optional
        A unique string name to identify the Physics object, typically same as
        instance name but can be anything.  If left blank, and name will be
        generated that include the class name and a random string.

    """

    def __init__(self, project=None, network=None, phase=None,
                 geometry=None, settings={}, **kwargs):

        # Define some default settings
        self.settings.update({'prefix': 'phys'})
        # Overwrite with user supplied settings, if any
        self.settings.update(settings)

        # Deal with network or project arguments
        if network is not None:
            if project is not None:
                assert network is project.network
            else:
                project = network.project

        super().__init__(project=project, **kwargs)

        network = self.project.network
        if network:
            if phase is None:
                raise Exception('All Physics objects must be associated ' +
                                'with a Phase')
            else:
                phase['pore.'+self.name] = False
                phase['throat.'+self.name] = False
            if geometry is None:
                geoms = self.project.geometries().values()
                if len(geoms) == 0:
                    pass
                elif len(geoms) == 1:
                    logger.info('No Geometry given, assigning ' + self.name +
                                ' to all pores and throats')
                else:
                    raise Exception('Multiple Geometry objects are defined; ' +
                                    'must specify which one ' + self.name +
                                    ' should be associated with')
                Ps = network.Ps
                Ts = network.Ts
            else:
                Ps = network.pores(geometry.name)
                Ts = network.throats(geometry.name)
            self._add_locations(pores=Ps, throats=Ts)
<|endoftext|>


<commit_before>import unittest
from mock import patch, Mock, call
from sandbox_scripts.environment.setup.setup_VM import EnvironmentSetupVM
from sandbox_scripts.QualiEnvironmentUtils.QualiUtils import QualiError
from cloudshell.api.common_cloudshell_api import CloudShellAPIError
from cloudshell.api.cloudshell_api import ResourceInfoVmDetails
import json
import os

resContext = '''{"id":"5487c6ce-d0b3-43e9-8ee7-e27af8406905",
 "ownerUser":"bob",
 "ownerPass":"nIqm+BG6ZGJjby5hUittVFFJASc=",
 "domain":"Global",
 "environmentName":"My environment",
 "description":"New demo environment",
 "parameters":
   { "globalInputs": [],
     "resourceRequirements":[],
     "resourceAdditionalInfo":[]}}'''

conContext = '''{"serverAddress": "localhost",
"adminAuthToken": "anAdminToken"}'''

class SetupVMTests(unittest.TestCase):

    @patch('sandbox_scripts.environment.setup.setup_VM.get_qs_logger')
    def setUp(self, mock_logger):
        os.environ['reservationContext'] = resContext
        os.environ['qualiConnectivityContext'] = conContext
        self.setup_script = EnvironmentSetupVM()

    def tearDown(self):
        pass

    @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session')
    @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase')
    @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager')
    def test_setup_vm_with_no_resources(self, mock_save, mock_sandboxbase, mock_api_session):

        self.setup_script.execute()

        report_info_calls = [call('Beginning VMs power on'),
                             call(message='No VMs to power on ', write_to_output_window=True)]
        mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls)

    @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session')
    @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase')
    @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager')
    def test_setup_vm_with_regular_resource(self, mock_save, mock_sandboxbase, mock_api_session):

        rdi = Mock()
        resource1 = Mock()
        resource1.Name = "r1"
        resource2 = Mock()
        resource2.Name = "r2"
        rdi.ReservationDescription.Resources = [resource1, resource2]
        rdi.ReservationDescription.TopologiesReservedResources = []
        rdi.ReservationDescription.Apps = []

        mock_sandboxbase.return_value.get_details.return_value = rdi

        def resource_details_mock_side_effect(name):
            rd = Mock()
            rd.details.Name = name
            rd.details.Address = ''
            if name == 'r1':
                rd.VmDetails = None
            elif name == 'r2':
                rd.VmDetails = object()
            return rd

        mock_sandboxbase.return_value.api_session.GetResourceDetails.side_effect = resource_details_mock_side_effect

        mock_save.return_value.is_snapshot.return_value = True

        self.setup_script.execute()

        report_info_calls = [call('Beginning VMs power on')]
        mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls)

        logger_debug_calls = [call("Skipping resource 'r1' - not an app, not powering on"),
                              call("Skipping resource 'r2' - not an app, not powering on")]
        self.setup_script.logger.debug.assert_has_calls(logger_debug_calls)

    @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session')
    @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase')
    @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager')
    def test_setup_vm_with_static_app_resource_power_on_no_wait_for_ip(self, mock_save, mock_sandboxbase, mock_api_session):
        rdi = Mock()
        resource1 = Mock()
        resource1.Name = "r2"
        resource1.ResourceModelName = 'vcenter static vm'
        rdi.ReservationDescription.Resources = [resource1]
        rdi.ReservationDescription.TopologiesReservedResources = []
        rdi.ReservationDescription.Apps = []

        mock_sandboxbase.return_value.get_details.return_value = rdi

        def resource_details_mock_side_effect(name):
            rd = Mock()
            rd.details.Name = name
            rd.details.Address = ''
            rd.VmDetails = Mock()
            rd.VmDetails.UID = 'abcd'
            return rd

        mock_sandboxbase.return_value.api_session.GetResourceDetails.side_effect = resource_details_mock_side_effect

        mock_save.return_value.is_snapshot.return_value = False

        self.setup_script.execute()

        report_info_calls = [call('Beginning VMs power on'),
                             call('Apps are powering on... '),
                             call(log_message="Executing 'Power On' on deployed app 'r2' in reservation "
                                              "'5487c6ce-d0b3-43e9-8ee7-e27af8406905'",
                                  message="Executing 'Power On' on deployed app 'r2' ", write_to_output_window=True)]
        mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls)

        logger_debug_calls = [call("Resource r2 is a static app")]
        self.setup_script.logger.debug.assert_has_calls(logger_debug_calls)

        logger_info_calls = [call("Wait For IP is off for deployed app 'r2' in reservation "
                                  "'5487c6ce-d0b3-43e9-8ee7-e27af8406905'")]
        self.setup_script.logger.info.assert_has_calls(logger_info_calls)

        api_calls = [call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'r2', 'PowerOn', 'power')]
        mock_sandboxbase.return_value.api_session.ExecuteResourceConnectedCommand.assert_has_calls(api_calls)

    @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session')
    @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase')
    @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager')
    def test_setup_vm_with_deployed_app_resource(self, mock_save, mock_sandboxbase, mock_api_session):
        rdi = Mock()
        resource1 = Mock()
        resource1.Name = "r2"
        resource1.ResourceModelName = 'deployed app'
        rdi.ReservationDescription.Resources = [resource1]
        rdi.ReservationDescription.TopologiesReservedResources = []
        rdi.ReservationDescription.Apps = []

        mock_sandboxbase.return_value.get_details.return_value = rdi

        def resource_details_mock_side_effect(name):
            rd = Mock()
            rd.details.Name = name
            rd.details.Address = ''
            rd.VmDetails = Mock()
            rd.VmDetails.UID = 'abcd'
            return rd

        mock_sandboxbase.return_value.api_session.GetResourceDetails.side_effect = resource_details_mock_side_effect

        mock_save.return_value.is_snapshot.return_value = True

        self.setup_script.execute()

        report_info_calls = [call('Beginning VMs power on'),
                             call('Apps are powering on... '),
                             call(log_message="Executing 'Power On' on deployed app 'r2' in reservation "
                                              "'5487c6ce-d0b3-43e9-8ee7-e27af8406905'",
                                  message="Executing 'Power On' on deployed app 'r2' ", write_to_output_window=True),
                             call(message='Waiting for apps IP addresses, this may take a while...',
                                  write_to_output_window=True)]
        mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls)

        api_calls = [call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'r2', 'PowerOn', 'power'),
                     call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'r2', 'remote_refresh_ip', 'remote_connectivity')]
        mock_sandboxbase.return_value.api_session.ExecuteResourceConnectedCommand.assert_has_calls(api_calls)

if __name__ == '__main__':
    unittest.main()
<commit_msg>Fix test (parallel may cause other order of calls)<commit_after>import unittest
from mock import patch, Mock, call
from sandbox_scripts.environment.setup.setup_VM import EnvironmentSetupVM
from sandbox_scripts.QualiEnvironmentUtils.QualiUtils import QualiError
from cloudshell.api.common_cloudshell_api import CloudShellAPIError
from cloudshell.api.cloudshell_api import ResourceInfoVmDetails
import json
import os

resContext = '''{"id":"5487c6ce-d0b3-43e9-8ee7-e27af8406905",
 "ownerUser":"bob",
 "ownerPass":"nIqm+BG6ZGJjby5hUittVFFJASc=",
 "domain":"Global",
 "environmentName":"My environment",
 "description":"New demo environment",
 "parameters":
   { "globalInputs": [],
     "resourceRequirements":[],
     "resourceAdditionalInfo":[]}}'''

conContext = '''{"serverAddress": "localhost",
"adminAuthToken": "anAdminToken"}'''

class SetupVMTests(unittest.TestCase):

    @patch('sandbox_scripts.environment.setup.setup_VM.get_qs_logger')
    def setUp(self, mock_logger):
        os.environ['reservationContext'] = resContext
        os.environ['qualiConnectivityContext'] = conContext
        self.setup_script = EnvironmentSetupVM()

    def tearDown(self):
        pass

    @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session')
    @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase')
    @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager')
    def test_setup_vm_with_no_resources(self, mock_save, mock_sandboxbase, mock_api_session):

        self.setup_script.execute()

        report_info_calls = [call('Beginning VMs power on'),
                             call(message='No VMs to power on ', write_to_output_window=True)]
        mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls)

    @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session')
    @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase')
    @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager')
    def test_setup_vm_with_regular_resource(self, mock_save, mock_sandboxbase, mock_api_session):

        rdi = Mock()
        resource1 = Mock()
        resource1.Name = "r1"
        resource2 = Mock()
        resource2.Name = "r2"
        rdi.ReservationDescription.Resources = [resource1, resource2]
        rdi.ReservationDescription.TopologiesReservedResources = []
        rdi.ReservationDescription.Apps = []

        mock_sandboxbase.return_value.get_details.return_value = rdi

        def resource_details_mock_side_effect(name):
            rd = Mock()
            rd.details.Name = name
            rd.details.Address = ''
            if name == 'r1':
                rd.VmDetails = None
            elif name == 'r2':
                rd.VmDetails = object()
            return rd

        mock_sandboxbase.return_value.api_session.GetResourceDetails.side_effect = resource_details_mock_side_effect

        mock_save.return_value.is_snapshot.return_value = True

        self.setup_script.execute()

        report_info_calls = [call('Beginning VMs power on')]
        mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls)

        logger_debug_calls = [call("Skipping resource 'r1' - not an app, not powering on"),
                              call("Skipping resource 'r2' - not an app, not powering on")]
        self.setup_script.logger.debug.assert_has_calls(logger_debug_calls, any_order=True)

    @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session')
    @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase')
    @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager')
    def test_setup_vm_with_static_app_resource_power_on_no_wait_for_ip(self, mock_save, mock_sandboxbase, mock_api_session):
        rdi = Mock()
        resource1 = Mock()
        resource1.Name = "r2"
        resource1.ResourceModelName = 'vcenter static vm'
        rdi.ReservationDescription.Resources = [resource1]
        rdi.ReservationDescription.TopologiesReservedResources = []
        rdi.ReservationDescription.Apps = []

        mock_sandboxbase.return_value.get_details.return_value = rdi

        def resource_details_mock_side_effect(name):
            rd = Mock()
            rd.details.Name = name
            rd.details.Address = ''
            rd.VmDetails = Mock()
            rd.VmDetails.UID = 'abcd'
            return rd

        mock_sandboxbase.return_value.api_session.GetResourceDetails.side_effect = resource_details_mock_side_effect

        mock_save.return_value.is_snapshot.return_value = False

        self.setup_script.execute()

        report_info_calls = [call('Beginning VMs power on'),
                             call('Apps are powering on... '),
                             call(log_message="Executing 'Power On' on deployed app 'r2' in reservation "
                                              "'5487c6ce-d0b3-43e9-8ee7-e27af8406905'",
                                  message="Executing 'Power On' on deployed app 'r2' ", write_to_output_window=True)]
        mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls)

        logger_debug_calls = [call("Resource r2 is a static app")]
        self.setup_script.logger.debug.assert_has_calls(logger_debug_calls)

        logger_info_calls = [call("Wait For IP is off for deployed app 'r2' in reservation "
                                  "'5487c6ce-d0b3-43e9-8ee7-e27af8406905'")]
        self.setup_script.logger.info.assert_has_calls(logger_info_calls)

        api_calls = [call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'r2', 'PowerOn', 'power')]
        mock_sandboxbase.return_value.api_session.ExecuteResourceConnectedCommand.assert_has_calls(api_calls)

    @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session')
    @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase')
    @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager')
    def test_setup_vm_with_deployed_app_resource(self, mock_save, mock_sandboxbase, mock_api_session):
        rdi = Mock()
        resource1 = Mock()
        resource1.Name = "r2"
        resource1.ResourceModelName = 'deployed app'
        rdi.ReservationDescription.Resources = [resource1]
        rdi.ReservationDescription.TopologiesReservedResources = []
        rdi.ReservationDescription.Apps = []

        mock_sandboxbase.return_value.get_details.return_value = rdi

        def resource_details_mock_side_effect(name):
            rd = Mock()
            rd.details.Name = name
            rd.details.Address = ''
            rd.VmDetails = Mock()
            rd.VmDetails.UID = 'abcd'
            return rd

        mock_sandboxbase.return_value.api_session.GetResourceDetails.side_effect = resource_details_mock_side_effect

        mock_save.return_value.is_snapshot.return_value = True

        self.setup_script.execute()

        report_info_calls = [call('Beginning VMs power on'),
                             call('Apps are powering on... '),
                             call(log_message="Executing 'Power On' on deployed app 'r2' in reservation "
                                              "'5487c6ce-d0b3-43e9-8ee7-e27af8406905'",
                                  message="Executing 'Power On' on deployed app 'r2' ", write_to_output_window=True),
                             call(message='Waiting for apps IP addresses, this may take a while...',
                                  write_to_output_window=True)]
        mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls)

        api_calls = [call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'r2', 'PowerOn', 'power'),
                     call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'r2', 'remote_refresh_ip', 'remote_connectivity')]
        mock_sandboxbase.return_value.api_session.ExecuteResourceConnectedCommand.assert_has_calls(api_calls)

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


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

from cgi import escape, FieldStorage
import cgitb
from dbclient import dbclient, dbcfg
from urllib import urlencode
import re

cgitb.enable()

per_page = 32
outdata = []
client = dbclient(dbcfg(None, [".wellpapprc"]))
fs = FieldStorage(keep_blank_values=True)
user = "fake"
base = unicode(client.cfg.webbase)
assert base
thumbsize = unicode(client.cfg.thumb_sizes.split()[0])
assert thumbsize

def getarg(n, default=None, as_list=False):
	if default is not None and n not in fs: return default
	a = fs[n]
	if isinstance(a, list):
		a = [_argdec(e.value) for e in a]
		if not as_list: a = u' '.join(a)
	else:
		a = _argdec(a.value)
		if as_list: a = a.split()
	return a
def _argdec(v):
	try:
		v = v.decode("utf-8")
	except Exception:
		v = v.decode("iso-8859-1")
	return v

def notfound():
	print "Status: 404 Not Found"
	print "Content-Type: text/html; charset=UTF-8"
	print
	print "404 Not Found"
	exit()

def clean(n):
	if n[0] in u"-~": return n[1:]
	return n
def prefix(n):
	if n[0] in u"-~": return n[0]
	return ""

def prt(*a):
	outdata.extend(a)

def prtfields(*fields):
	map(lambda f: prt(f[0], u'="', escape(unicode(f[1])), u'" '), fields)

_zwsp_pre_re = re.compile(ur'([(<\[]|\b\d)')
_zwsp_post_re = re.compile(ur'([:/)>\]&\\,\._-])')
_zwsp_nr_re = re.compile(ur'(\d+)')
_zwsp_re = re.compile(ur'\u200b+')
def tagfmt(n, html_ok=True):
	n = _zwsp_pre_re.sub(u'\u200b\\1', n)
	n = _zwsp_post_re.sub(u'\\1\u200b', n)
	n = _zwsp_nr_re.sub(u'\u200b\\1\u200b', n)
	n = _zwsp_re.sub(u'\u200b', n)
	return escape(n)

def tagname(guid):
	tag = client.get_tag(guid)
	return tag.name

def taglist(post, impl):
	if impl:
		full, weak = post.impltags, post.implweaktags
	else:
		full, weak = post.tags, post.weaktags
	full = [(tagfmt(t.name), t, impl) for t in full]
	weak = [(tagfmt(u'~' + t.name), t, impl) for t in weak]
	return full + weak

def tagcloud(guids):
	guids = set(guids)
	posts = client.search_post(guids=guids, wanted=["tagname", "tagguid", "tagdata"])[0]
	tags = {}
	guid = {}
	for p in posts:
		for t in p.tags:
			tags[t.guid] = t
			guid[t.guid] = guid.get(t.guid, 0) + 1
	show = sorted(guid, lambda x, y: cmp(guid[y], guid[x]))
	show = [g for g in show[:20 + len(guids)] if g not in guids]
	return [(tagfmt(tags[g].name), tags[g], False) for g in show]

def tagtypes():
	# The server doesn't support this yet, so fake it for now
	return [u'unspecified', u'ambiguous', u'meta', u'inimage',
	        u'photographer', u'person', u'location', u'text', u'group']

def tag_post(p, full, weak, remove):
	post_full = set([t.guid for t in p.tags])
	post_weak = set([t.guid for t in p.weaktags])
	set_full = full.difference(post_full)
	set_weak = weak.difference(post_weak)
	set_remove_full = post_full.intersection(remove)
	set_remove_weak = post_weak.intersection(remove)
	set_remove = set_remove_full.union(set_remove_weak)
	if set_full or set_weak or set_remove:
		client.tag_post(p.md5, full_tags=set_full, weak_tags=set_weak, remove_tags=set_remove)
		return True

def prt_tags(tags):
	if not tags: return
	prt(u'
')
	for n, t, impl in tags:
		c = u'tag implied' if impl else u'tag'
		prt(u'
', n, u'
\n')
	prt(u'
')

def prt_posts(posts):
	prt(u'
\n')
	for post in posts:
		m = post.md5
		prt(u'\n')
	prt(u'
\n')

def tags_as_html(post):
	names = sorted([t.name for t in post.tags or []])
	names += sorted([u'~' + t.name for t in post.weaktags or []])
	return u' '.join([tagfmt(n, False) for n in names])

def prt_search_form(q=u''):
	prt(u'<form action="', base, u'search" method="get">\n',
	    u'
\n',
	    u'<input type="text" name="q" id="search-q" value="', escape(q, True),
	    u'" onfocus="WP.comp_init(this);" />\n',
	    u'<input type="submit" value="Search" />\n',
	    u'
\n',
	    u'</form>\n')

def makelink(fn, *args):
	fn = base + fn
	if not args: return fn
	if u'?' in fn:
		middle = u'&'
	else:
		middle = u'?'
	args = urlencode([(a, v.encode("utf-8")) for a, v in args])
	return fn + middle + escape(args)

def pagelinks(link, page, result_count):
	global outdata
	pages = range(result_count // per_page + 1)
	if len(pages) == 1:
		if not user: return u''
		pages = []
	real_outdata = outdata
	outdata = []
	if len(pages) > 16:
		if page < 8:
			pages = pages[:10] + pages[-6:]
		elif page > len(pages) - 8:
			pages = pages[:6] + pages[-10:]
		else:
			pages = pages[:6] + pages[page - 2:page + 3] + pages[-5:]
	prt(u'
')
	prev = -1
	for p in pages:
		if p != prev + 1:
			prt(u'...\n')
		prev = p
		prt(u'')
		else:
			prt(u'">')
		prt(unicode(p))
		if p != page:
			prt(u'')
		prt(u'\n')
	if user:
		if pages:
			prt(u'ALL\n')
		prt(u'',
		    u'Tagmode\n')
	prt(u'
\n')
	res = u''.join(outdata)
	outdata = real_outdata
	return res

def prt_tagform(m):
	prt(u'<form action="' + base + u'post-tag" method="post">\n',
	    u'
\n',
	    u'<input type="hidden" name="post" value="' + m + u'" />\n',
	    u'<input type="text" name="tags" id="tag-q" ',
	    u' onfocus="WP.comp_init(this);" />\n',
	    u'<input type="submit" value="Tag" />\n',
	    u'
\n',
	    u'</form>\n')

def prt_script(script, suffix=u'\n'):
	prt('', suffix)

def prt_left_head():
	prt(u'
\n')

def prt_left_foot():
	prt(u'
Help
\n',
	    u'
\n')

def prt_head(extra_script=None):
	prt(u"""<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

	<title>WWWwellpapp</title>
	<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
	<link rel="stylesheet" href="%(base)sstatic/style.css" />
	<link rel="stylesheet" href="%(base)sstatic/tagstyle.css" />

	""" % {"base": base})
	if extra_script:
		prt(u'\n\t')
		prt_script(extra_script, u'');
	prt(u'\n\n\n')
	if user:
		prt(u'

\n')

def prt_foot():
	prt_script(u'complete.js')
	if user: prt_script(u'tagmode.js')
	prt(u'')

def finish(ctype = "text/html"):
	data = u''.join(outdata).encode("utf-8")
	ctype = str(ctype)
	if (ctype[:5] == "text/" or ctype == "application/json") and "charset" not in ctype:
		ctype += "; charset=UTF-8"
	print "Content-Type: " + ctype
	print "Content-Length: " + str(len(data) + 1)
	print
	print data
<commit_msg>Put link rel=first, last, next, prev, help where it makes sense.<commit_after># -*- coding: utf-8 -*-

from cgi import escape, FieldStorage
import cgitb
from dbclient import dbclient, dbcfg
from urllib import urlencode
import re

cgitb.enable()

per_page = 32
outdata_head = []
outdata = []
client = dbclient(dbcfg(None, [".wellpapprc"]))
fs = FieldStorage(keep_blank_values=True)
user = "fake"
base = unicode(client.cfg.webbase)
assert base
thumbsize = unicode(client.cfg.thumb_sizes.split()[0])
assert thumbsize

def getarg(n, default=None, as_list=False):
	if default is not None and n not in fs: return default
	a = fs[n]
	if isinstance(a, list):
		a = [_argdec(e.value) for e in a]
		if not as_list: a = u' '.join(a)
	else:
		a = _argdec(a.value)
		if as_list: a = a.split()
	return a
def _argdec(v):
	try:
		v = v.decode("utf-8")
	except Exception:
		v = v.decode("iso-8859-1")
	return v

def notfound():
	print "Status: 404 Not Found"
	print "Content-Type: text/html; charset=UTF-8"
	print
	print "404 Not Found"
	exit()

def clean(n):
	if n[0] in u"-~": return n[1:]
	return n
def prefix(n):
	if n[0] in u"-~": return n[0]
	return ""

def prt(*a):
	outdata.extend(a)

def prtfields(*fields):
	map(lambda f: prt(f[0], u'="', escape(unicode(f[1])), u'" '), fields)

_zwsp_pre_re = re.compile(ur'([(<\[]|\b\d)')
_zwsp_post_re = re.compile(ur'([:/)>\]&\\,\._-])')
_zwsp_nr_re = re.compile(ur'(\d+)')
_zwsp_re = re.compile(ur'\u200b+')
def tagfmt(n, html_ok=True):
	n = _zwsp_pre_re.sub(u'\u200b\\1', n)
	n = _zwsp_post_re.sub(u'\\1\u200b', n)
	n = _zwsp_nr_re.sub(u'\u200b\\1\u200b', n)
	n = _zwsp_re.sub(u'\u200b', n)
	return escape(n)

def tagname(guid):
	tag = client.get_tag(guid)
	return tag.name

def taglist(post, impl):
	if impl:
		full, weak = post.impltags, post.implweaktags
	else:
		full, weak = post.tags, post.weaktags
	full = [(tagfmt(t.name), t, impl) for t in full]
	weak = [(tagfmt(u'~' + t.name), t, impl) for t in weak]
	return full + weak

def tagcloud(guids):
	guids = set(guids)
	posts = client.search_post(guids=guids, wanted=["tagname", "tagguid", "tagdata"])[0]
	tags = {}
	guid = {}
	for p in posts:
		for t in p.tags:
			tags[t.guid] = t
			guid[t.guid] = guid.get(t.guid, 0) + 1
	show = sorted(guid, lambda x, y: cmp(guid[y], guid[x]))
	show = [g for g in show[:20 + len(guids)] if g not in guids]
	return [(tagfmt(tags[g].name), tags[g], False) for g in show]

def tagtypes():
	# The server doesn't support this yet, so fake it for now
	return [u'unspecified', u'ambiguous', u'meta', u'inimage',
	        u'photographer', u'person', u'location', u'text', u'group']

def tag_post(p, full, weak, remove):
	post_full = set([t.guid for t in p.tags])
	post_weak = set([t.guid for t in p.weaktags])
	set_full = full.difference(post_full)
	set_weak = weak.difference(post_weak)
	set_remove_full = post_full.intersection(remove)
	set_remove_weak = post_weak.intersection(remove)
	set_remove = set_remove_full.union(set_remove_weak)
	if set_full or set_weak or set_remove:
		client.tag_post(p.md5, full_tags=set_full, weak_tags=set_weak, remove_tags=set_remove)
		return True

def prt_tags(tags):
	if not tags: return
	prt(u'
')
	for n, t, impl in tags:
		c = u'tag implied' if impl else u'tag'
		prt(u'
', n, u'
\n')
	prt(u'
')

def prt_posts(posts):
	prt(u'
\n')
	for post in posts:
		m = post.md5
		prt(u'\n')
	prt(u'
\n')

def tags_as_html(post):
	names = sorted([t.name for t in post.tags or []])
	names += sorted([u'~' + t.name for t in post.weaktags or []])
	return u' '.join([tagfmt(n, False) for n in names])

def prt_search_form(q=u''):
	prt(u'<form action="', base, u'search" method="get">\n',
	    u'
\n',
	    u'<input type="text" name="q" id="search-q" value="', escape(q, True),
	    u'" onfocus="WP.comp_init(this);" />\n',
	    u'<input type="submit" value="Search" />\n',
	    u'
\n',
	    u'</form>\n')

def makelink(fn, *args):
	fn = base + fn
	if not args: return fn
	if u'?' in fn:
		middle = u'&'
	else:
		middle = u'?'
	args = urlencode([(a, v.encode("utf-8")) for a, v in args])
	return fn + middle + escape(args)

def pagelinks(link, page, result_count):
	global outdata
	pages = range(result_count // per_page + 1)
	if len(pages) == 1:
		if not user: return u''
		pages = []
	real_outdata = outdata
	outdata = []
	if len(pages) > 16:
		if page < 8:
			pages = pages[:10] + pages[-6:]
		elif page > len(pages) - 8:
			pages = pages[:6] + pages[-10:]
		else:
			pages = pages[:6] + pages[page - 2:page + 3] + pages[-5:]
	prt(u'
')
	prev = -1
	for p in pages:
		if p != prev + 1:
			prt(u'...\n')
		prev = p
		prt(u'')
		else:
			prt(u'">')
		prt(unicode(p))
		if p != page:
			prt(u'')
		prt(u'\n')
		if p == 0:
			prt_rel(plink, "first")
		if p == page - 1:
			prt_rel(plink, "prev")
		elif p == page + 1:
			prt_rel(plink, "next")
	if pages: prt_rel(plink, "last")
	if user:
		if pages:
			prt(u'ALL\n')
		prt(u'',
		    u'Tagmode\n')
	prt(u'
\n')
	res = u''.join(outdata)
	outdata = real_outdata
	return res

def prt_tagform(m):
	prt(u'<form action="' + base + u'post-tag" method="post">\n',
	    u'
\n',
	    u'<input type="hidden" name="post" value="' + m + u'" />\n',
	    u'<input type="text" name="tags" id="tag-q" ',
	    u' onfocus="WP.comp_init(this);" />\n',
	    u'<input type="submit" value="Tag" />\n',
	    u'
\n',
	    u'</form>\n')

def prt_script(script, suffix=u'\n'):
	prt('', suffix)

def prt_left_head():
	prt(u'
\n')

def prt_left_foot():
	prt(u'
Help
\n',
	    u'
\n')

def prt_head(extra_script=None):
	global outdata
	outdata = outdata_head
	prt(u"""<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

	<title>WWWwellpapp</title>
	<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
	<link rel="stylesheet" href="%(base)sstatic/style.css" />
	<link rel="stylesheet" href="%(base)sstatic/tagstyle.css" />

	<link rel="help" href="%(base)sstatic/help.html" />""" % {"base": base})
	if extra_script:
		prt(u'\n\t')
		prt_script(extra_script, u'');
	outdata = []
	prt(u'\n\n\n')
	if user:
		prt(u'

\n')

def prt_rel(href, rel):
	if "ALL" in fs: return
	global outdata
	real_outdata = outdata
	outdata = outdata_head
	prt(u'\n\t<link rel="', rel, u'" href="', href, u'" />')
	outdata = real_outdata

def prt_foot():
	prt_script(u'complete.js')
	if user: prt_script(u'tagmode.js')
	prt(u'')

def finish(ctype = "text/html"):
	data = u''.join(outdata_head + outdata).encode("utf-8")
	ctype = str(ctype)
	if (ctype[:5] == "text/" or ctype == "application/json") and "charset" not in ctype:
		ctype += "; charset=UTF-8"
	print "Content-Type: " + ctype
	print "Content-Length: " + str(len(data) + 1)
	print
	print data
<|endoftext|>


<commit_before>import pprint
import functools

# This look-up table, initialised upon the first call to `from_json`, maps CGum
# AST types to their representative Python classes, using the type ID.
__CODE_CLASS_LOOKUP = {}

# Constructs the AST class look-up table. Used by `lookup_table` to build the
# look-up table upon its first request.
def __build_lookup_table(typ):
    if hasattr(typ, 'CODE'):
        #print("Registering AST type [%s]: %s" % (typ.CODE, typ.__name__))
        __CODE_CLASS_LOOKUP[typ.CODE] = typ
    for sub_typ in typ.__subclasses__():
        __build_lookup_table(sub_typ)

# Used to return the type ID to Python class look-up table.
# Responsible for overseeing the construction of the look-up table on its first
# invocation.
def lookup_table():
    if not __CODE_CLASS_LOOKUP:
        __build_lookup_table(Node)
    return __CODE_CLASS_LOOKUP

# The base class used by all AST nodes
class Node(object):
    # Constructs an AST node from a given JSON definition
    @staticmethod
    def from_json(jsn):
        assert 'type' in jsn, "expected 'type' property in AST node"
        typid = jsn['type']
        try:
            typ = lookup_table()[typid]
        except KeyError as e:
            raise Exception(("no Python representation of AST node with type %s found." +\
                             "Has CGum type label: %s.") % (typid, jsn['typeLabel']))
        #print("Converting AST node of (Python) type: %s" % typ.__name__)

        # Build the children of this node, if there are any, then extract any
        # attached label
        children = [Node.from_json(c) for c in jsn['children']]
        label = jsn['label'] if 'label' in jsn else None

        # Construct a node of the correct type, using the extracted information
        return typ(jsn['pos'], jsn['length'], label, children)

    def __init__(self, pos, length, label, children):
        self.__pos = int(pos)
        self.__length = int(length)
        self.__label = label
        self.__children = children
        self.__number = None
        self.__depth = None
        self.__size = None
        self.__numberStart = None
        self.__parent = None
        self.__hash = None

    # Determines whether this node is equivalent to a given node.
    def equivalent(self, other):
        return  type(self) == type(other) and\
                self.__label == other.label() and\
                len(self.__children) == len(other.children()) and\
                all([c1.equivalent(c2) for (c1, c2) in zip(self.__children, other.children())])

    # By default nodes are not statements, unless overridden by another class.
    def is_statement(self):
        return False

    # Collects all nodes within the sub-tree rooted at this node which satisfy a
    # given predicate (in the form of a lambda expression)
    def collect(self, pred):
        found = [self] if pred(self) else []
        for c in self.__children:
            found += c.collect(pred)
        return found

    # Generates a hash for this node, based on its type, label, and the hashes
    # of its children. Used for cheap equivalency checking.
    def __hash__(self):
        if self.__hash is None:
            h_a = hash(tuple(hash(c) for c in self.__children))
            h_b = hash(self.__class__.__name__)
            if self.__label is None:
                h = (h_a, h_b)
            else:
                h = (h_a, h_b, self.__label)
            self.__hash = hash(h)
        return self.__hash

    # Returns a copy of the AST sub-tree rooted at this node with its variable
    # names replaced by placeholders
    def strip_variable_names(self):
        cp = self.copy()
        cp.__children = [child.strip_variable_names() for child in cp.children()]
        return cp

    def parent(self):
        return self.__parent
    def label(self):
        return self.__label
    def pos(self):
        return self.__pos
    def length(self):
        return self.__length
    def children(self):
        return self.__children
    def number(self):
        return self.__number
    def numberRange(self):
        return (self.__numberStart, self.__number)
    def size(self):
        return self.__size
    def depth(self):
        return self.__depth
    def child(self, i):
        return self.__children[i]

    def set_label(self, label):
        self.__label = label

    # Returns the index of a given child of this node.
    # If the given node is not a child of this node, returns None.
    # Throws an assertion error if the child is None.
    def index_of_child(self, child):
        assert not child is None, "indexOfChild: child must not be None"
        for (i, c) in enumerate(self.children()):
            if c == child:
                return i
        return None

    # Returns a list of all the descendants of this node, in order from nearest
    # to furthest.
    def descendants(self):
        return functools.reduce(lambda d, c: d + [c] + c.descendants,\
                      self.__children,\
                      [])

    # Returns a list of all the ancestors of this node, in order from nearest
    # to furthest.
    def ancestors(self):
        if self.__parent:
            return [self.__parent] + self.__parent.ancestors()
        else:
            return []

    # Returns the nearest statement to this node.
    # By default, we find the nearest statement to the parent node, if there
    # is one. If not, None is returned.
    # Statements implement a different nearestStmt, which returns the statement
    # in question.
    def nearestStmt(self):
        return self.__parent.nearestStmt() if self.__parent else None

    # Returns the index of the range that a given node belongs to, or None if
    # it belongs to none of them.
    def __find_helper(self, num, ranges, mn, mx):
        if mx < mn:
            return -1
        pivot = (mn + mx) // 2
        (num_start, num_end) = ranges[pivot]
        if num < num_start:
            return self.__find_helper(num, ranges, mn, pivot - 1)
        elif num > num_end:
            return self.__find_helper(num, ranges, pivot + 1, mx)
        else:
            return pivot

    # Finds a node with a given number within the sub-tree rooted at this node.
    def find(self, num):
        assert not num is None, "find: num must not be None"
        if self.__number == num:
            return self
        ind = self.__find_helper(num,\
                                 [c.numberRange() for c in self.__children],\
                                 0, len(self.__children) - 1)
        return self.__children[ind].find(num) if ind >= 0 else None

    def find_all(self, predicate):
        res = [self] if predicate(self) else []
        return functools.reduce(lambda r, c: r + c.find_all(predicate),\
                                self.__children, res)

    # Returns the node of the function that this node belongs to, or None if it
    # doesn't belong to a function (i.e. it's a top-level statement).

    # Pretty-prints the tree rooted at this node; useful for debugging
    def pp(self, depth=0):
        print("%s%s - %d" % ((" " * depth), self.__class__.__name__, self.__number))
        depth += 2
        for c in self.__children:
            c.pp(depth=depth)

    # Returns the CGum AST type label for this node
    def typeLabel(self):
        return self.__class__.LABEL

    # Recursively renumbers all nodes belonging to the sub-tree rooted at this
    # node. Numbers start at zero. Unfortunately necessary, since the CGum AST
    # output doesn't provide node numbers.
    def renumber(self, num=0, parent=None):
        self.__numberStart = num
        self.__parent = parent
        for c in self.__children:
            num = c.renumber(num, self)
        #print("Assigning number %d to %s at %d" % (num, self.typeLabel(), self.__pos))
        self.__number = num
        return num + 1

    def to_s(self):
        raise NotImplementedError('No `to_s` method exists for this object')
    def to_json(self):
        raise NotImplementedError('No `to_json` method exists for this object')

class Token(Node):
    def __init__(self, pos, length, label, children):
        assert not children
        assert not label
        super().__init__(pos, length, label, children)

class GenericList(Node):
    CODE = "470000"
    LABEL = "GenericList"

    def contents(self):
        return self.children()
    def __len__(self):
        return len(self.children())

class GenericString(Node):
    CODE = "480000"
    LABEL = "GenericString"

    def read(self):
        return self.label()
    def to_s(self):
        return self.label()

# :-(
class NotParsedCorrectly(Node):
    CODE = "450700"
    LABEL = "NotParsedCorrectly"

# Strangely, None can have children
class NoneNode(Node):
    CODE = "290001"
    LABEL = "None"

    def __init__(self, pos, length, label, children):
        assert label is None
        assert len(children) == 1
        assert isinstance(children[0], GenericString)
        super().__init__(pos, length, label, children)

# Equally, I have no idea what the Left node is for?
class Left(Node):
    CODE = "20100"
    LABEL = "Left"

    def __init__(self, pos, length, label, children):
        assert label is None
        assert len(children) == 1
        super().__init__(pos, length, label, children)
<commit_msg>modified strip_variable_names<commit_after>import pprint
import copy
import functools

# This look-up table, initialised upon the first call to `from_json`, maps CGum
# AST types to their representative Python classes, using the type ID.
__CODE_CLASS_LOOKUP = {}

# Constructs the AST class look-up table. Used by `lookup_table` to build the
# look-up table upon its first request.
def __build_lookup_table(typ):
    if hasattr(typ, 'CODE'):
        #print("Registering AST type [%s]: %s" % (typ.CODE, typ.__name__))
        __CODE_CLASS_LOOKUP[typ.CODE] = typ
    for sub_typ in typ.__subclasses__():
        __build_lookup_table(sub_typ)

# Used to return the type ID to Python class look-up table.
# Responsible for overseeing the construction of the look-up table on its first
# invocation.
def lookup_table():
    if not __CODE_CLASS_LOOKUP:
        __build_lookup_table(Node)
    return __CODE_CLASS_LOOKUP

# The base class used by all AST nodes
class Node(object):
    # Constructs an AST node from a given JSON definition
    @staticmethod
    def from_json(jsn):
        assert 'type' in jsn, "expected 'type' property in AST node"
        typid = jsn['type']
        try:
            typ = lookup_table()[typid]
        except KeyError as e:
            raise Exception(("no Python representation of AST node with type %s found." +\
                             "Has CGum type label: %s.") % (typid, jsn['typeLabel']))
        #print("Converting AST node of (Python) type: %s" % typ.__name__)

        # Build the children of this node, if there are any, then extract any
        # attached label
        children = [Node.from_json(c) for c in jsn['children']]
        label = jsn['label'] if 'label' in jsn else None

        # Construct a node of the correct type, using the extracted information
        return typ(jsn['pos'], jsn['length'], label, children)

    def __init__(self, pos, length, label, children):
        self.__pos = int(pos)
        self.__length = int(length)
        self.__label = label
        self.__children = children
        self.__number = None
        self.__depth = None
        self.__size = None
        self.__numberStart = None
        self.__parent = None
        self.__hash = None

    # Determines whether this node is equivalent to a given node.
    def equivalent(self, other):
        return  type(self) == type(other) and\
                self.__label == other.label() and\
                len(self.__children) == len(other.children()) and\
                all([c1.equivalent(c2) for (c1, c2) in zip(self.__children, other.children())])

    # By default nodes are not statements, unless overridden by another class.
    def is_statement(self):
        return False

    # Collects all nodes within the sub-tree rooted at this node which satisfy a
    # given predicate (in the form of a lambda expression)
    def collect(self, pred):
        found = [self] if pred(self) else []
        for c in self.__children:
            found += c.collect(pred)
        return found

    # Generates a hash for this node, based on its type, label, and the hashes
    # of its children. Used for cheap equivalency checking.
    def __hash__(self):
        if self.__hash is None:
            h_a = hash(tuple(hash(c) for c in self.__children))
            h_b = hash(self.__class__.__name__)
            if self.__label is None:
                h = (h_a, h_b)
            else:
                h = (h_a, h_b, self.__label)
            self.__hash = hash(h)
        return self.__hash

    # Returns a copy of the AST sub-tree rooted at this node with its variable
    # names replaced by placeholders
    def strip_variable_names(self):
        cp = copy.copy(self)
        cp.__children = [child.strip_variable_names() for child in cp.children()]
        return cp

    def parent(self):
        return self.__parent
    def label(self):
        return self.__label
    def pos(self):
        return self.__pos
    def length(self):
        return self.__length
    def children(self):
        return self.__children
    def number(self):
        return self.__number
    def numberRange(self):
        return (self.__numberStart, self.__number)
    def size(self):
        return self.__size
    def depth(self):
        return self.__depth
    def child(self, i):
        return self.__children[i]

    def set_label(self, label):
        self.__label = label

    # Returns the index of a given child of this node.
    # If the given node is not a child of this node, returns None.
    # Throws an assertion error if the child is None.
    def index_of_child(self, child):
        assert not child is None, "indexOfChild: child must not be None"
        for (i, c) in enumerate(self.children()):
            if c == child:
                return i
        return None

    # Returns a list of all the descendants of this node, in order from nearest
    # to furthest.
    def descendants(self):
        return functools.reduce(lambda d, c: d + [c] + c.descendants,\
                      self.__children,\
                      [])

    # Returns a list of all the ancestors of this node, in order from nearest
    # to furthest.
    def ancestors(self):
        if self.__parent:
            return [self.__parent] + self.__parent.ancestors()
        else:
            return []

    # Returns the nearest statement to this node.
    # By default, we find the nearest statement to the parent node, if there
    # is one. If not, None is returned.
    # Statements implement a different nearestStmt, which returns the statement
    # in question.
    def nearestStmt(self):
        return self.__parent.nearestStmt() if self.__parent else None

    # Returns the index of the range that a given node belongs to, or None if
    # it belongs to none of them.
    def __find_helper(self, num, ranges, mn, mx):
        if mx < mn:
            return -1
        pivot = (mn + mx) // 2
        (num_start, num_end) = ranges[pivot]
        if num < num_start:
            return self.__find_helper(num, ranges, mn, pivot - 1)
        elif num > num_end:
            return self.__find_helper(num, ranges, pivot + 1, mx)
        else:
            return pivot

    # Finds a node with a given number within the sub-tree rooted at this node.
    def find(self, num):
        assert not num is None, "find: num must not be None"
        if self.__number == num:
            return self
        ind = self.__find_helper(num,\
                                 [c.numberRange() for c in self.__children],\
                                 0, len(self.__children) - 1)
        return self.__children[ind].find(num) if ind >= 0 else None

    def find_all(self, predicate):
        res = [self] if predicate(self) else []
        return functools.reduce(lambda r, c: r + c.find_all(predicate),\
                                self.__children, res)

    # Returns the node of the function that this node belongs to, or None if it
    # doesn't belong to a function (i.e. it's a top-level statement).

    # Pretty-prints the tree rooted at this node; useful for debugging
    def pp(self, depth=0):
        print("%s%s - %d" % ((" " * depth), self.__class__.__name__, self.__number))
        depth += 2
        for c in self.__children:
            c.pp(depth=depth)

    # Returns the CGum AST type label for this node
    def typeLabel(self):
        return self.__class__.LABEL

    # Recursively renumbers all nodes belonging to the sub-tree rooted at this
    # node. Numbers start at zero. Unfortunately necessary, since the CGum AST
    # output doesn't provide node numbers.
    def renumber(self, num=0, parent=None):
        self.__numberStart = num
        self.__parent = parent
        for c in self.__children:
            num = c.renumber(num, self)
        #print("Assigning number %d to %s at %d" % (num, self.typeLabel(), self.__pos))
        self.__number = num
        return num + 1

    def to_s(self):
        raise NotImplementedError('No `to_s` method exists for this object')
    def to_json(self):
        raise NotImplementedError('No `to_json` method exists for this object')

class Token(Node):
    def __init__(self, pos, length, label, children):
        assert not children
        assert not label
        super().__init__(pos, length, label, children)

class GenericList(Node):
    CODE = "470000"
    LABEL = "GenericList"

    def contents(self):
        return self.children()
    def __len__(self):
        return len(self.children())

class GenericString(Node):
    CODE = "480000"
    LABEL = "GenericString"

    def read(self):
        return self.label()
    def to_s(self):
        return self.label()

# :-(
class NotParsedCorrectly(Node):
    CODE = "450700"
    LABEL = "NotParsedCorrectly"

# Strangely, None can have children
class NoneNode(Node):
    CODE = "290001"
    LABEL = "None"

    def __init__(self, pos, length, label, children):
        assert label is None
        assert len(children) == 1
        assert isinstance(children[0], GenericString)
        super().__init__(pos, length, label, children)

# Equally, I have no idea what the Left node is for?
class Left(Node):
    CODE = "20100"
    LABEL = "Left"

    def __init__(self, pos, length, label, children):
        assert label is None
        assert len(children) == 1
        super().__init__(pos, length, label, children)
<|endoftext|>