<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2014 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
#
from multiprocessing import Event

import os
import shutil

import unittest
import sys
from qubes.qubes import QubesVmCollection, QubesException
from qubes import backup

VM_PREFIX = "test-"

class BackupTests(unittest.TestCase):
    def setUp(self):
        self.error_detected = Event()
        self.verbose = False
        self.qc = QubesVmCollection()
        self.qc.lock_db_for_writing()
        self.qc.load()

        if self.verbose:
            print >>sys.stderr, "-> Creating backupvm"
        backupvm = self.qc.add_new_vm("QubesAppVm",
                           name="%sbackupvm" % VM_PREFIX,
                           template=self.qc.get_default_template())
        backupvm.create_on_disk(verbose=self.verbose)
        self.qc.save()
        self.qc.unlock_db()

        self.backupdir = os.path.join(os.environ["HOME"], "test-backup")
        os.mkdir(self.backupdir)

    def tearDown(self):
        vmlist = [vm for vm in self.qc.values() if vm.name.startswith(
            VM_PREFIX)]
        self.remove_vms(vmlist)
        shutil.rmtree(self.backupdir)

    def print_progress(self, progress):
        if self.verbose:
            print >> sys.stderr, "\r-> Backing up files: {0}%...".format(progress)

    def error_callback(self, message):
        self.error_detected.set()
        if self.verbose:
            print >> sys.stderr, "ERROR: {0}".format(message)

    def print_callback(self, msg):
        if self.verbose:
            print msg

    def fill_image(self, path, size=None, sparse=False):
        block_size = 4096

        if self.verbose:
            print >>sys.stderr, "-> Filling %s" % path
        f = open(path, 'w+')
        if size is None:
            f.seek(0, 2)
            size = f.tell()
        f.seek(0)

        for block_num in xrange(size/block_size):
            f.write('a' * block_size)
            if sparse:
                f.seek(block_size, 1)

        f.close()

    def create_basic_vms(self, leave_locked=False):
        template=self.qc.get_default_template()

        vms = []
        self.qc.lock_db_for_writing()
        self.qc.load()
        vmname = "%stest1" % VM_PREFIX
        if self.verbose:
            print >>sys.stderr, "-> Creating %s" % vmname
        testvm1 = self.qc.add_new_vm("QubesAppVm",
                                     name=vmname,
                                     template=template)
        testvm1.create_on_disk(verbose=self.verbose)
        vms.append(testvm1)
        self.fill_image(testvm1.private_img, 100*1024*1024)

        vmname = "%stesthvm1" % VM_PREFIX
        if self.verbose:
            print >>sys.stderr, "-> Creating %s" % vmname
        testvm2 = self.qc.add_new_vm("QubesHVm",
                                     name=vmname)
        testvm2.create_on_disk(verbose=self.verbose)
        self.fill_image(testvm2.root_img, 1024*1024*1024, True)
        vms.append(testvm2)

        if not leave_locked:
            self.qc.save()
            self.qc.unlock_db()

        return vms

    def remove_vms(self, vms):
        self.qc.lock_db_for_writing()
        self.qc.load()

        for vm in vms:
            if isinstance(vm, str):
                vm = self.qc.get_vm_by_name(vm)
            else:
                vm = self.qc[vm.qid]
            if self.verbose:
                print >>sys.stderr, "-> Removing %s" % vm.name
            vm.remove_from_disk()
            self.qc.pop(vm.qid)
        self.qc.save()
        self.qc.unlock_db()

    def print_callback(self, msg):
        if self.verbose:
            print msg

    def make_backup(self, vms, prepare_kwargs=dict(), do_kwargs=dict()):
        try:
            files_to_backup = \
                backup.backup_prepare(vms,
                                      print_callback=self.print_callback,
                                      **prepare_kwargs)
        except QubesException as e:
            self.fail("QubesException during backup_prepare: %s" % str(e))

        try:
            backup.backup_do(self.backupdir, files_to_backup, "qubes",
                             progress_callback=self.print_progress,
                             **do_kwargs)
        except QubesException as e:
            self.fail("QubesException during backup_do: %s" % str(e))

    def restore_backup(self):
        backupfile = os.path.join(self.backupdir,
                                  sorted(os.listdir(self.backupdir))[-1])

        try:
            backup_info = backup.backup_restore_prepare(
                backupfile, "qubes", print_callback=self.print_callback)
        except QubesException as e: self.fail(
                "QubesException during backup_restore_prepare: %s" % str(e))
        if self.verbose:
            backup.backup_restore_print_summary(backup_info)

        try:
            backup.backup_restore_do(
                backup_info,
                print_callback=self.print_callback,
                error_callback=self.error_callback)
                # TODO: print_callback=self.print_callback if self.verbose else None,
        except QubesException as e:
            self.fail("QubesException during backup_restore_do: %s" % str(e))
        self.assertFalse(self.error_detected.is_set(),
                         "Error detected during backup_restore_do")
        os.unlink(backupfile)

    def test_basic_backup(self):
        vms = self.create_basic_vms()
        self.make_backup(vms)
        self.remove_vms(vms)
        self.restore_backup()
        self.remove_vms(vms)

    def test_compressed_backup(self):
        vms = self.create_basic_vms()
        self.make_backup(vms, do_kwargs={'compressed': True})
        self.remove_vms(vms)
        self.restore_backup()
        self.remove_vms(vms)

    def test_encrypted_backup(self):
        vms = self.create_basic_vms()
        self.make_backup(vms, do_kwargs={'encrypted': True})
        self.remove_vms(vms)
        self.restore_backup()
        self.remove_vms(vms)

    @unittest.expectedFailure
    def test_compressed_encrypted_backup(self):
        vms = self.create_basic_vms()
        self.make_backup(vms,
                         do_kwargs={
                             'compressed': True,
                             'encrypted': True})
        self.remove_vms(vms)
        self.restore_backup()
        self.remove_vms(vms)

    def test_sparse_multipart(self):
        vms = []
        self.qc.lock_db_for_writing()
        self.qc.load()

        vmname = "%stesthvm2" % VM_PREFIX
        if self.verbose:
            print >>sys.stderr, "-> Creating %s" % vmname

        hvmtemplate = self.qc.add_new_vm("QubesTemplateHVm",
                                         name=vmname)
        hvmtemplate.create_on_disk(verbose=self.verbose)
        self.fill_image(os.path.join(hvmtemplate.dir_path, '00file'),
                        195*1024*1024-4096*3)
        self.fill_image(hvmtemplate.private_img, 195*1024*1024-4096*3)
        self.fill_image(hvmtemplate.root_img, 1024*1024*1024, sparse=True)
        vms.append(hvmtemplate)

        self.qc.save()
        self.qc.unlock_db()

        self.make_backup(vms)
        self.remove_vms(vms)
        self.restore_backup()
        self.remove_vms(vms)

<commit_msg>tests: improve error reporting in backup tests<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2014 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
#
from multiprocessing import Event, Queue

import os
import shutil

import unittest
import sys
from qubes.qubes import QubesVmCollection, QubesException
from qubes import backup

VM_PREFIX = "test-"

class BackupTests(unittest.TestCase):
    def setUp(self):
        self.error_detected = Queue()
        self.verbose = False
        self.qc = QubesVmCollection()
        self.qc.lock_db_for_writing()
        self.qc.load()

        if self.verbose:
            print >>sys.stderr, "-> Creating backupvm"
        backupvm = self.qc.add_new_vm("QubesAppVm",
                           name="%sbackupvm" % VM_PREFIX,
                           template=self.qc.get_default_template())
        backupvm.create_on_disk(verbose=self.verbose)
        self.qc.save()
        self.qc.unlock_db()

        self.backupdir = os.path.join(os.environ["HOME"], "test-backup")
        os.mkdir(self.backupdir)

    def tearDown(self):
        vmlist = [vm for vm in self.qc.values() if vm.name.startswith(
            VM_PREFIX)]
        self.remove_vms(vmlist)
        shutil.rmtree(self.backupdir)

    def print_progress(self, progress):
        if self.verbose:
            print >> sys.stderr, "\r-> Backing up files: {0}%...".format(progress)

    def error_callback(self, message):
        self.error_detected.put(message)
        if self.verbose:
            print >> sys.stderr, "ERROR: {0}".format(message)

    def print_callback(self, msg):
        if self.verbose:
            print msg

    def fill_image(self, path, size=None, sparse=False):
        block_size = 4096

        if self.verbose:
            print >>sys.stderr, "-> Filling %s" % path
        f = open(path, 'w+')
        if size is None:
            f.seek(0, 2)
            size = f.tell()
        f.seek(0)

        for block_num in xrange(size/block_size):
            f.write('a' * block_size)
            if sparse:
                f.seek(block_size, 1)

        f.close()

    def create_basic_vms(self, leave_locked=False):
        template=self.qc.get_default_template()

        vms = []
        self.qc.lock_db_for_writing()
        self.qc.load()
        vmname = "%stest1" % VM_PREFIX
        if self.verbose:
            print >>sys.stderr, "-> Creating %s" % vmname
        testvm1 = self.qc.add_new_vm("QubesAppVm",
                                     name=vmname,
                                     template=template)
        testvm1.create_on_disk(verbose=self.verbose)
        vms.append(testvm1)
        self.fill_image(testvm1.private_img, 100*1024*1024)

        vmname = "%stesthvm1" % VM_PREFIX
        if self.verbose:
            print >>sys.stderr, "-> Creating %s" % vmname
        testvm2 = self.qc.add_new_vm("QubesHVm",
                                     name=vmname)
        testvm2.create_on_disk(verbose=self.verbose)
        self.fill_image(testvm2.root_img, 1024*1024*1024, True)
        vms.append(testvm2)

        if not leave_locked:
            self.qc.save()
            self.qc.unlock_db()

        return vms

    def remove_vms(self, vms):
        self.qc.lock_db_for_writing()
        self.qc.load()

        for vm in vms:
            if isinstance(vm, str):
                vm = self.qc.get_vm_by_name(vm)
            else:
                vm = self.qc[vm.qid]
            if self.verbose:
                print >>sys.stderr, "-> Removing %s" % vm.name
            vm.remove_from_disk()
            self.qc.pop(vm.qid)
        self.qc.save()
        self.qc.unlock_db()

    def make_backup(self, vms, prepare_kwargs=dict(), do_kwargs=dict()):
        try:
            files_to_backup = \
                backup.backup_prepare(vms,
                                      print_callback=self.print_callback,
                                      **prepare_kwargs)
        except QubesException as e:
            self.fail("QubesException during backup_prepare: %s" % str(e))

        try:
            backup.backup_do(self.backupdir, files_to_backup, "qubes",
                             progress_callback=self.print_progress,
                             **do_kwargs)
        except QubesException as e:
            self.fail("QubesException during backup_do: %s" % str(e))

    def restore_backup(self):
        backupfile = os.path.join(self.backupdir,
                                  sorted(os.listdir(self.backupdir))[-1])

        try:
            backup_info = backup.backup_restore_prepare(
                backupfile, "qubes", print_callback=self.print_callback)
        except QubesException as e: self.fail(
                "QubesException during backup_restore_prepare: %s" % str(e))
        if self.verbose:
            backup.backup_restore_print_summary(backup_info)

        try:
            backup.backup_restore_do(
                backup_info,
                print_callback=self.print_callback,
                error_callback=self.error_callback)
                # TODO: print_callback=self.print_callback if self.verbose else None,
        except QubesException as e:
            self.fail("QubesException during backup_restore_do: %s" % str(e))
        errors = []
        while not self.error_detected.empty():
            errors.append(self.error_detected.get())
        self.assertTrue(len(errors) == 0,
                         "Error(s) detected during backup_restore_do: %s" %
                         '\n'.join(errors))
        os.unlink(backupfile)

    def test_basic_backup(self):
        vms = self.create_basic_vms()
        self.make_backup(vms)
        self.remove_vms(vms)
        self.restore_backup()
        self.remove_vms(vms)

    def test_compressed_backup(self):
        vms = self.create_basic_vms()
        self.make_backup(vms, do_kwargs={'compressed': True})
        self.remove_vms(vms)
        self.restore_backup()
        self.remove_vms(vms)

    def test_encrypted_backup(self):
        vms = self.create_basic_vms()
        self.make_backup(vms, do_kwargs={'encrypted': True})
        self.remove_vms(vms)
        self.restore_backup()
        self.remove_vms(vms)

    @unittest.expectedFailure
    def test_compressed_encrypted_backup(self):
        vms = self.create_basic_vms()
        self.make_backup(vms,
                         do_kwargs={
                             'compressed': True,
                             'encrypted': True})
        self.remove_vms(vms)
        self.restore_backup()
        self.remove_vms(vms)

    def test_sparse_multipart(self):
        vms = []
        self.qc.lock_db_for_writing()
        self.qc.load()

        vmname = "%stesthvm2" % VM_PREFIX
        if self.verbose:
            print >>sys.stderr, "-> Creating %s" % vmname

        hvmtemplate = self.qc.add_new_vm("QubesTemplateHVm",
                                         name=vmname)
        hvmtemplate.create_on_disk(verbose=self.verbose)
        self.fill_image(os.path.join(hvmtemplate.dir_path, '00file'),
                        195*1024*1024-4096*3)
        self.fill_image(hvmtemplate.private_img, 195*1024*1024-4096*3)
        self.fill_image(hvmtemplate.root_img, 1024*1024*1024, sparse=True)
        vms.append(hvmtemplate)

        self.qc.save()
        self.qc.unlock_db()

        self.make_backup(vms)
        self.remove_vms(vms)
        self.restore_backup()
        self.remove_vms(vms)

<|endoftext|>


<commit_before># stdlib
import time
from hashlib import md5
import urllib2

# project
from checks import AgentCheck
from util import headers

# 3rd party
import simplejson as json
import requests

class Marathon(AgentCheck):
    def check(self, instance):
        if 'url' not in instance:
            raise Exception('Marathon instance missing "url" value.')

        # Load values from the instance config
        url = instance['url']
        instance_tags = instance.get('tags', [])
        default_timeout = self.init_config.get('default_timeout', 5)
        timeout = float(instance.get('timeout', default_timeout))

        response = self.get_v2_apps(url, timeout)
        if response is not None:
            self.gauge('marathon.apps', len(response['apps']), tags=instance_tags)
            for app in response['apps']:
                tags = ['app_id:' + app['id'], 'version:' + app['version']] + instance_tags
                for attr in ['taskRateLimit','instances','cpus','mem','tasksStaged','tasksRunning']:
                    self.gauge('marathon.' + attr, app[attr], tags=tags)
                versions_reply = self.get_v2_app_versions(url, app['id'], timeout)
                if versions_reply is not None:
                    self.gauge('marathon.versions', len(versions_reply['versions']), tags=tags)

    def get_v2_apps(self, url, timeout):
        # Use a hash of the URL as an aggregation key
        aggregation_key = md5(url).hexdigest()
        try:
            r = requests.get(url + "/v2/apps", timeout=timeout)
        except requests.exceptions.Timeout as e:
            # If there's a timeout
            self.timeout_event(url, timeout, aggregation_key)
            raise Exception("Timeout when hitting %s" % url)

        if r.status_code != 200:
            self.status_code_event(url, r, aggregation_key)
            raise Exception("Got %s when hitting %s" % (r.status_code, url))

        # Condition for request v1.x backward compatibility
        if hasattr(r.json, '__call__'):
            return r.json()
        else:
            return r.json

    def get_v2_app_versions(self, url, app_id, timeout):
        # Use a hash of the URL as an aggregation key
        aggregation_key = md5(url).hexdigest()

        try:
            r = requests.get(url + "/v2/apps/" + app_id + "/versions", timeout=timeout)
        except requests.exceptions.Timeout as e:
            # If there's a timeout
            self.timeout_event(url, timeout, aggregation_key)
            self.warning("Timeout when hitting %s" % url)
            return None

        if r.status_code != 200:
            self.status_code_event(url, r, aggregation_key)
            self.warning("Got %s when hitting %s" % (r.status_code, url))
            return None

        return r.json()

    def timeout_event(self, url, timeout, aggregation_key):
        self.event({
            'timestamp': int(time.time()),
            'event_type': 'http_check',
            'msg_title': 'URL timeout',
            'msg_text': '%s timed out after %s seconds.' % (url, timeout),
            'aggregation_key': aggregation_key
        })

    def status_code_event(self, url, r, aggregation_key):
        self.event({
            'timestamp': int(time.time()),
            'event_type': 'http_check',
            'msg_title': 'Invalid reponse code for %s' % url,
            'msg_text': '%s returned a status of %s' % (url, r.status_code),
            'aggregation_key': aggregation_key
        })
<commit_msg>add svc at url connection<commit_after># stdlib
import time
from hashlib import md5
import urllib2

# project
from checks import AgentCheck
from util import headers

# 3rd party
import simplejson as json
import requests
import logging
log = logging.getLogger('collector')

class Marathon(AgentCheck):

    check_name = "marathon.can_connect"

    def check(self, instance):
        if 'url' not in instance:
            raise Exception('Marathon instance missing "url" value.')

        # Load values from the instance config
        url = instance['url']
        instance_tags = instance.get('tags', [])
        default_timeout = self.init_config.get('default_timeout', 5)
        timeout = float(instance.get('timeout', default_timeout))

        response = self.get_v2_apps(url, timeout)
        if response is not None:
            self.gauge('marathon.apps', len(response['apps']), tags=instance_tags)
            for app in response['apps']:
                tags = ['app_id:' + app['id'], 'version:' + app['version']] + instance_tags
                for attr in ['taskRateLimit','instances','cpus','mem','tasksStaged','tasksRunning']:
                    self.gauge('marathon.' + attr, app[attr], tags=tags)
                versions_reply = self.get_v2_app_versions(url, app, timeout)
                if versions_reply is not None:
                    self.gauge('marathon.versions', len(versions_reply['versions']), tags=tags)

    def get_v2_apps(self, url, timeout):
        # Use a hash of the URL as an aggregation key
        aggregation_key = md5(url).hexdigest()
        tags = ['url:%s' % url]

        try:
            r = requests.get(url + "/v2/apps", timeout=timeout)
            status = AgentCheck.OK
            if r.status_code != 200:
                self.status_code_event(url, r, aggregation_key)
                status = AgentCheck.CRITICAL
                msg = "Got %s when hitting %s" % (r.status_code, url)
                self.warning(msg)
        except requests.exceptions.Timeout as e:
            # If there's a timeout
            self.timeout_event(url, timeout, aggregation_key)
            msg = "Timeout when hitting %s" % url
            status = AgentCheck.CRITICAL
            self.warning(msg)
        except Exception as e:
            msg = e.message
            status = AgentCheck.CRITICAL
        finally:
            self.service_check(self.check_name, status, tags=tags, message=msg)
            if status is AgentCheck.CRITICAL:
                raise Exception(msg)

        # Condition for request v1.x backward compatibility
        if hasattr(r.json, '__call__'):
            return r.json()
        else:
            return r.json

    def get_v2_app_versions(self, url, app, timeout):
        # Use a hash of the URL as an aggregation key
        aggregation_key = md5(url).hexdigest()

        tags = ['url:%s' % url, 'app_id:%s' % app['id'], 'version:%s' % app['version']]

        try:
            r = requests.get(url + "/v2/apps/" + app_id + "/versions", timeout=timeout)
            status = AgentCheck.OK
            if r.status_code != 200:
                self.status_code_event(url, r, aggregation_key)
                status = AgentCheck.CRITICAL
                msg = "Got %s when hitting %s" % (r.status_code, url)
                self.warning(msg)
        except requests.exceptions.Timeout as e:
            # If there's a timeout
            msg = "Timeout when hitting %s" % url
            self.timeout_event(url, timeout, aggregation_key)
            status = AgentCheck.CRITICAL
            self.warning(msg)
        except Exception as e:
            msg = e.message
            status = AgentCheck.CRITICAL
        finally:
            self.service_check(self.check_name, status, tags=tags, message=msg)
            if status is AgentCheck.CRITICAL:
                return None

        return r.json()

    def timeout_event(self, url, timeout, aggregation_key):
        self.event({
            'timestamp': int(time.time()),
            'event_type': 'http_check',
            'msg_title': 'URL timeout',
            'msg_text': '%s timed out after %s seconds.' % (url, timeout),
            'aggregation_key': aggregation_key
        })

    def status_code_event(self, url, r, aggregation_key):
        self.event({
            'timestamp': int(time.time()),
            'event_type': 'http_check',
            'msg_title': 'Invalid reponse code for %s' % url,
            'msg_text': '%s returned a status of %s' % (url, r.status_code),
            'aggregation_key': aggregation_key
        })
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from openerp.tests import common

class TestSaleCouponCommon(common.TransactionCase):

    def setUp(self):
        super(TestSaleCouponCommon, self).setUp()

        # Set all the existing programs to active=False to avoid interference
        self.env['sale.coupon.program'].search([]).write({'active': False})

        # create partner for sale order.
        self.steve = self.env['res.partner'].create({
            'name': 'Steve Bucknor',
            'customer': True,
            'email': 'steve.bucknor@example.com',
        })

        self.empty_order = self.env['sale.order'].create({
            'partner_id': self.steve.id
        })

        self.uom_unit = self.env.ref('product.product_uom_unit')

        #products
        self.product_A = self.env['product.product'].create({
            'name': 'Product A',
            'list_price': 100,
            'sale_ok': True
        })

        self.product_B = self.env['product.product'].create({
            'name': 'Product B',
            'list_price': 5,
            'sale_ok': True
        })

        # Immediate Program By A + B: get B free
        # No Conditions
        self.immediate_promotion_program = self.env['sale.coupon.program'].create({
            'name': 'Buy A + 1 B, 1 B are free',
            'promo_code_usage': 'no_code_needed',
            'reward_type': 'product',
            'reward_product_id': self.product_B.id,
            'rule_products_domain': "[('id', 'in', [%s])]" % (self.product_A.id),
            'active': True,
        })

        self.code_promotion_program = self.env['sale.coupon.program'].create({
            'name': 'Buy 1 A + Enter code, 1 A is free',
            'promo_code_usage': 'code_needed',
            'reward_type': 'product',
            'reward_product_id': self.product_A.id,
            'rule_products_domain': "[('id', 'in', [%s])]" % (self.product_A.id),
            'active': True,
        })
<commit_msg>[Imp] product: move uom in a new addon<commit_after># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from openerp.tests import common

class TestSaleCouponCommon(common.TransactionCase):

    def setUp(self):
        super(TestSaleCouponCommon, self).setUp()

        # Set all the existing programs to active=False to avoid interference
        self.env['sale.coupon.program'].search([]).write({'active': False})

        # create partner for sale order.
        self.steve = self.env['res.partner'].create({
            'name': 'Steve Bucknor',
            'customer': True,
            'email': 'steve.bucknor@example.com',
        })

        self.empty_order = self.env['sale.order'].create({
            'partner_id': self.steve.id
        })

        self.uom_unit = self.env.ref('uom.product_uom_unit')

        #products
        self.product_A = self.env['product.product'].create({
            'name': 'Product A',
            'list_price': 100,
            'sale_ok': True
        })

        self.product_B = self.env['product.product'].create({
            'name': 'Product B',
            'list_price': 5,
            'sale_ok': True
        })

        # Immediate Program By A + B: get B free
        # No Conditions
        self.immediate_promotion_program = self.env['sale.coupon.program'].create({
            'name': 'Buy A + 1 B, 1 B are free',
            'promo_code_usage': 'no_code_needed',
            'reward_type': 'product',
            'reward_product_id': self.product_B.id,
            'rule_products_domain': "[('id', 'in', [%s])]" % (self.product_A.id),
            'active': True,
        })

        self.code_promotion_program = self.env['sale.coupon.program'].create({
            'name': 'Buy 1 A + Enter code, 1 A is free',
            'promo_code_usage': 'code_needed',
            'reward_type': 'product',
            'reward_product_id': self.product_A.id,
            'rule_products_domain': "[('id', 'in', [%s])]" % (self.product_A.id),
            'active': True,
        })
<|endoftext|>


<commit_before>"""Native adapter for serving CherryPy via mod_python

Basic usage:

##########################################
# Application in a module called myapp.py
##########################################

import cherrypy

class Root:
    @cherrypy.expose
    def index(self):
        return 'Hi there, Ho there, Hey there'

# We will use this method from the mod_python configuration
# as the entry point to our application
def setup_server():
    cherrypy.tree.mount(Root())
    cherrypy.config.update({'environment': 'production',
                            'log.screen': False,
                            'show_tracebacks': False})

##########################################
# mod_python settings for apache2
# This should reside in your httpd.conf
# or a file that will be loaded at
# apache startup
##########################################

# Start
DocumentRoot "/"
Listen 8080
LoadModule python_module /usr/lib/apache2/modules/mod_python.so

<Location "/">
    PythonPath "sys.path+['/path/to/my/application']"
    SetHandler python-program
    PythonHandler cherrypy._cpmodpy::handler
    PythonOption cherrypy.setup myapp::setup_server
    PythonDebug On
</Location>
# End

The actual path to your mod_python.so is dependent on your
environment. In this case we suppose a global mod_python
installation on a Linux distribution such as Ubuntu.

We do set the PythonPath configuration setting so that
your application can be found by from the user running
the apache2 instance. Of course if your application
resides in the global site-package this won't be needed.

Then restart apache2 and access http://127.0.0.1:8080
"""

import io
import logging
import os
import re
import sys

import six

import cherrypy
from cherrypy._cpcompat import text_or_bytes
from cherrypy._cperror import format_exc, bare_error
from cherrypy.lib import httputil

# ------------------------------ Request-handling

def setup(req):
    from mod_python import apache

    # Run any setup functions defined by a "PythonOption cherrypy.setup"
    # directive.
    options = req.get_options()
    if 'cherrypy.setup' in options:
        for function in options['cherrypy.setup'].split():
            atoms = function.split('::', 1)
            if len(atoms) == 1:
                mod = __import__(atoms[0], globals(), locals())
            else:
                modname, fname = atoms
                mod = __import__(modname, globals(), locals(), [fname])
                func = getattr(mod, fname)
                func()

    cherrypy.config.update({'log.screen': False,
                            'tools.ignore_headers.on': True,
                            'tools.ignore_headers.headers': ['Range'],
                            })

    engine = cherrypy.engine
    if hasattr(engine, 'signal_handler'):
        engine.signal_handler.unsubscribe()
    if hasattr(engine, 'console_control_handler'):
        engine.console_control_handler.unsubscribe()
    engine.autoreload.unsubscribe()
    cherrypy.server.unsubscribe()

    @engine.subscribe('log')
    def _log(msg, level):
        newlevel = apache.APLOG_ERR
        if logging.DEBUG >= level:
            newlevel = apache.APLOG_DEBUG
        elif logging.INFO >= level:
            newlevel = apache.APLOG_INFO
        elif logging.WARNING >= level:
            newlevel = apache.APLOG_WARNING
        # On Windows, req.server is required or the msg will vanish. See
        # http://www.modpython.org/pipermail/mod_python/2003-October/014291.html
        # Also, "When server is not specified...LogLevel does not apply..."
        apache.log_error(msg, newlevel, req.server)

    engine.start()

    def cherrypy_cleanup(data):
        engine.exit()
    try:
        # apache.register_cleanup wasn't available until 3.1.4.
        apache.register_cleanup(cherrypy_cleanup)
    except AttributeError:
        req.server.register_cleanup(req, cherrypy_cleanup)

class _ReadOnlyRequest:
    expose = ('read', 'readline', 'readlines')

    def __init__(self, req):
        for method in self.expose:
            self.__dict__[method] = getattr(req, method)

recursive = False

_isSetUp = False

def handler(req):
    from mod_python import apache
    try:
        global _isSetUp
        if not _isSetUp:
            setup(req)
            _isSetUp = True

        # Obtain a Request object from CherryPy
        local = req.connection.local_addr
        local = httputil.Host(
            local[0], local[1], req.connection.local_host or '')
        remote = req.connection.remote_addr
        remote = httputil.Host(
            remote[0], remote[1], req.connection.remote_host or '')

        scheme = req.parsed_uri[0] or 'http'
        req.get_basic_auth_pw()

        try:
            # apache.mpm_query only became available in mod_python 3.1
            q = apache.mpm_query
            threaded = q(apache.AP_MPMQ_IS_THREADED)
            forked = q(apache.AP_MPMQ_IS_FORKED)
        except AttributeError:
            bad_value = ("You must provide a PythonOption '%s', "
                         "either 'on' or 'off', when running a version "
                         'of mod_python < 3.1')

            options = req.get_options()

            threaded = options.get('multithread', '').lower()
            if threaded == 'on':
                threaded = True
            elif threaded == 'off':
                threaded = False
            else:
                raise ValueError(bad_value % 'multithread')

            forked = options.get('multiprocess', '').lower()
            if forked == 'on':
                forked = True
            elif forked == 'off':
                forked = False
            else:
                raise ValueError(bad_value % 'multiprocess')

        sn = cherrypy.tree.script_name(req.uri or '/')
        if sn is None:
            send_response(req, '404 Not Found', [], '')
        else:
            app = cherrypy.tree.apps[sn]
            method = req.method
            path = req.uri
            qs = req.args or ''
            reqproto = req.protocol
            headers = list(six.iteritems(req.headers_in))
            rfile = _ReadOnlyRequest(req)
            prev = None

            try:
                redirections = []
                while True:
                    request, response = app.get_serving(local, remote, scheme,
                                                        'HTTP/1.1')
                    request.login = req.user
                    request.multithread = bool(threaded)
                    request.multiprocess = bool(forked)
                    request.app = app
                    request.prev = prev

                    # Run the CherryPy Request object and obtain the response
                    try:
                        request.run(method, path, qs, reqproto, headers, rfile)
                        break
                    except cherrypy.InternalRedirect:
                        ir = sys.exc_info()[1]
                        app.release_serving()
                        prev = request

                        if not recursive:
                            if ir.path in redirections:
                                raise RuntimeError(
                                    'InternalRedirector visited the same URL '
                                    'twice: %r' % ir.path)
                            else:
                                # Add the *previous* path_info + qs to
                                # redirections.
                                if qs:
                                    qs = '?' + qs
                                redirections.append(sn + path + qs)

                        # Munge environment and try again.
                        method = 'GET'
                        path = ir.path
                        qs = ir.query_string
                        rfile = io.BytesIO()

                send_response(
                    req, response.output_status, response.header_list,
                    response.body, response.stream)
            finally:
                app.release_serving()
    except Exception:
        tb = format_exc()
        cherrypy.log(tb, 'MOD_PYTHON', severity=logging.ERROR)
        s, h, b = bare_error()
        send_response(req, s, h, b)
    return apache.OK

def send_response(req, status, headers, body, stream=False):
    # Set response status
    req.status = int(status[:3])

    # Set response headers
    req.content_type = 'text/plain'
    for header, value in headers:
        if header.lower() == 'content-type':
            req.content_type = value
            continue
        req.headers_out.add(header, value)

    if stream:
        # Flush now so the status and headers are sent immediately.
        req.flush()

    # Set response body
    if isinstance(body, text_or_bytes):
        req.write(body)
    else:
        for seg in body:
            req.write(seg)

# --------------- Startup tools for CherryPy + mod_python --------------- #
try:
    import subprocess

    def popen(fullcmd):
        p = subprocess.Popen(fullcmd, shell=True,
                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                             close_fds=True)
        return p.stdout
except ImportError:
    def popen(fullcmd):
        pipein, pipeout = os.popen4(fullcmd)
        return pipeout

def read_process(cmd, args=''):
    fullcmd = '%s %s' % (cmd, args)
    pipeout = popen(fullcmd)
    try:
        firstline = pipeout.readline()
        cmd_not_found = re.search(
            b'(not recognized|No such file|not found)',
            firstline,
            re.IGNORECASE
        )
        if cmd_not_found:
            raise IOError('%s must be on your system path.' % cmd)
        output = firstline + pipeout.read()
    finally:
        pipeout.close()
    return output

class ModPythonServer(object):

    template = """
# Apache2 server configuration file for running CherryPy with mod_python.

DocumentRoot "/"
Listen %(port)s
LoadModule python_module modules/mod_python.so

<Location %(loc)s>
    SetHandler python-program
    PythonHandler %(handler)s
    PythonDebug On
%(opts)s
</Location>
"""

    def __init__(self, loc='/', port=80, opts=None, apache_path='apache',
                 handler='cherrypy._cpmodpy::handler'):
        self.loc = loc
        self.port = port
        self.opts = opts
        self.apache_path = apache_path
        self.handler = handler

    def start(self):
        opts = ''.join(['    PythonOption %s %s\n' % (k, v)
                        for k, v in self.opts])
        conf_data = self.template % {'port': self.port,
                                     'loc': self.loc,
                                     'opts': opts,
                                     'handler': self.handler,
                                     }

        mpconf = os.path.join(os.path.dirname(__file__), 'cpmodpy.conf')
        f = open(mpconf, 'wb')
        try:
            f.write(conf_data)
        finally:
            f.close()

        response = read_process(self.apache_path, '-k start -f %s' % mpconf)
        self.ready = True
        return response

    def stop(self):
        os.popen('apache -k stop')
        self.ready = False
<commit_msg>Re-use logic from always_iterable<commit_after>"""Native adapter for serving CherryPy via mod_python

Basic usage:

##########################################
# Application in a module called myapp.py
##########################################

import cherrypy

class Root:
    @cherrypy.expose
    def index(self):
        return 'Hi there, Ho there, Hey there'

# We will use this method from the mod_python configuration
# as the entry point to our application
def setup_server():
    cherrypy.tree.mount(Root())
    cherrypy.config.update({'environment': 'production',
                            'log.screen': False,
                            'show_tracebacks': False})

##########################################
# mod_python settings for apache2
# This should reside in your httpd.conf
# or a file that will be loaded at
# apache startup
##########################################

# Start
DocumentRoot "/"
Listen 8080
LoadModule python_module /usr/lib/apache2/modules/mod_python.so

<Location "/">
    PythonPath "sys.path+['/path/to/my/application']"
    SetHandler python-program
    PythonHandler cherrypy._cpmodpy::handler
    PythonOption cherrypy.setup myapp::setup_server
    PythonDebug On
</Location>
# End

The actual path to your mod_python.so is dependent on your
environment. In this case we suppose a global mod_python
installation on a Linux distribution such as Ubuntu.

We do set the PythonPath configuration setting so that
your application can be found by from the user running
the apache2 instance. Of course if your application
resides in the global site-package this won't be needed.

Then restart apache2 and access http://127.0.0.1:8080
"""

import io
import logging
import os
import re
import sys

import six

from more_itertools import always_iterable

import cherrypy
from cherrypy._cperror import format_exc, bare_error
from cherrypy.lib import httputil

# ------------------------------ Request-handling

def setup(req):
    from mod_python import apache

    # Run any setup functions defined by a "PythonOption cherrypy.setup"
    # directive.
    options = req.get_options()
    if 'cherrypy.setup' in options:
        for function in options['cherrypy.setup'].split():
            atoms = function.split('::', 1)
            if len(atoms) == 1:
                mod = __import__(atoms[0], globals(), locals())
            else:
                modname, fname = atoms
                mod = __import__(modname, globals(), locals(), [fname])
                func = getattr(mod, fname)
                func()

    cherrypy.config.update({'log.screen': False,
                            'tools.ignore_headers.on': True,
                            'tools.ignore_headers.headers': ['Range'],
                            })

    engine = cherrypy.engine
    if hasattr(engine, 'signal_handler'):
        engine.signal_handler.unsubscribe()
    if hasattr(engine, 'console_control_handler'):
        engine.console_control_handler.unsubscribe()
    engine.autoreload.unsubscribe()
    cherrypy.server.unsubscribe()

    @engine.subscribe('log')
    def _log(msg, level):
        newlevel = apache.APLOG_ERR
        if logging.DEBUG >= level:
            newlevel = apache.APLOG_DEBUG
        elif logging.INFO >= level:
            newlevel = apache.APLOG_INFO
        elif logging.WARNING >= level:
            newlevel = apache.APLOG_WARNING
        # On Windows, req.server is required or the msg will vanish. See
        # http://www.modpython.org/pipermail/mod_python/2003-October/014291.html
        # Also, "When server is not specified...LogLevel does not apply..."
        apache.log_error(msg, newlevel, req.server)

    engine.start()

    def cherrypy_cleanup(data):
        engine.exit()
    try:
        # apache.register_cleanup wasn't available until 3.1.4.
        apache.register_cleanup(cherrypy_cleanup)
    except AttributeError:
        req.server.register_cleanup(req, cherrypy_cleanup)

class _ReadOnlyRequest:
    expose = ('read', 'readline', 'readlines')

    def __init__(self, req):
        for method in self.expose:
            self.__dict__[method] = getattr(req, method)

recursive = False

_isSetUp = False

def handler(req):
    from mod_python import apache
    try:
        global _isSetUp
        if not _isSetUp:
            setup(req)
            _isSetUp = True

        # Obtain a Request object from CherryPy
        local = req.connection.local_addr
        local = httputil.Host(
            local[0], local[1], req.connection.local_host or '')
        remote = req.connection.remote_addr
        remote = httputil.Host(
            remote[0], remote[1], req.connection.remote_host or '')

        scheme = req.parsed_uri[0] or 'http'
        req.get_basic_auth_pw()

        try:
            # apache.mpm_query only became available in mod_python 3.1
            q = apache.mpm_query
            threaded = q(apache.AP_MPMQ_IS_THREADED)
            forked = q(apache.AP_MPMQ_IS_FORKED)
        except AttributeError:
            bad_value = ("You must provide a PythonOption '%s', "
                         "either 'on' or 'off', when running a version "
                         'of mod_python < 3.1')

            options = req.get_options()

            threaded = options.get('multithread', '').lower()
            if threaded == 'on':
                threaded = True
            elif threaded == 'off':
                threaded = False
            else:
                raise ValueError(bad_value % 'multithread')

            forked = options.get('multiprocess', '').lower()
            if forked == 'on':
                forked = True
            elif forked == 'off':
                forked = False
            else:
                raise ValueError(bad_value % 'multiprocess')

        sn = cherrypy.tree.script_name(req.uri or '/')
        if sn is None:
            send_response(req, '404 Not Found', [], '')
        else:
            app = cherrypy.tree.apps[sn]
            method = req.method
            path = req.uri
            qs = req.args or ''
            reqproto = req.protocol
            headers = list(six.iteritems(req.headers_in))
            rfile = _ReadOnlyRequest(req)
            prev = None

            try:
                redirections = []
                while True:
                    request, response = app.get_serving(local, remote, scheme,
                                                        'HTTP/1.1')
                    request.login = req.user
                    request.multithread = bool(threaded)
                    request.multiprocess = bool(forked)
                    request.app = app
                    request.prev = prev

                    # Run the CherryPy Request object and obtain the response
                    try:
                        request.run(method, path, qs, reqproto, headers, rfile)
                        break
                    except cherrypy.InternalRedirect:
                        ir = sys.exc_info()[1]
                        app.release_serving()
                        prev = request

                        if not recursive:
                            if ir.path in redirections:
                                raise RuntimeError(
                                    'InternalRedirector visited the same URL '
                                    'twice: %r' % ir.path)
                            else:
                                # Add the *previous* path_info + qs to
                                # redirections.
                                if qs:
                                    qs = '?' + qs
                                redirections.append(sn + path + qs)

                        # Munge environment and try again.
                        method = 'GET'
                        path = ir.path
                        qs = ir.query_string
                        rfile = io.BytesIO()

                send_response(
                    req, response.output_status, response.header_list,
                    response.body, response.stream)
            finally:
                app.release_serving()
    except Exception:
        tb = format_exc()
        cherrypy.log(tb, 'MOD_PYTHON', severity=logging.ERROR)
        s, h, b = bare_error()
        send_response(req, s, h, b)
    return apache.OK

def send_response(req, status, headers, body, stream=False):
    # Set response status
    req.status = int(status[:3])

    # Set response headers
    req.content_type = 'text/plain'
    for header, value in headers:
        if header.lower() == 'content-type':
            req.content_type = value
            continue
        req.headers_out.add(header, value)

    if stream:
        # Flush now so the status and headers are sent immediately.
        req.flush()

    # Set response body
    for seg in always_iterable(body):
        req.write(seg)

# --------------- Startup tools for CherryPy + mod_python --------------- #
try:
    import subprocess

    def popen(fullcmd):
        p = subprocess.Popen(fullcmd, shell=True,
                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                             close_fds=True)
        return p.stdout
except ImportError:
    def popen(fullcmd):
        pipein, pipeout = os.popen4(fullcmd)
        return pipeout

def read_process(cmd, args=''):
    fullcmd = '%s %s' % (cmd, args)
    pipeout = popen(fullcmd)
    try:
        firstline = pipeout.readline()
        cmd_not_found = re.search(
            b'(not recognized|No such file|not found)',
            firstline,
            re.IGNORECASE
        )
        if cmd_not_found:
            raise IOError('%s must be on your system path.' % cmd)
        output = firstline + pipeout.read()
    finally:
        pipeout.close()
    return output

class ModPythonServer(object):

    template = """
# Apache2 server configuration file for running CherryPy with mod_python.

DocumentRoot "/"
Listen %(port)s
LoadModule python_module modules/mod_python.so

<Location %(loc)s>
    SetHandler python-program
    PythonHandler %(handler)s
    PythonDebug On
%(opts)s
</Location>
"""

    def __init__(self, loc='/', port=80, opts=None, apache_path='apache',
                 handler='cherrypy._cpmodpy::handler'):
        self.loc = loc
        self.port = port
        self.opts = opts
        self.apache_path = apache_path
        self.handler = handler

    def start(self):
        opts = ''.join(['    PythonOption %s %s\n' % (k, v)
                        for k, v in self.opts])
        conf_data = self.template % {'port': self.port,
                                     'loc': self.loc,
                                     'opts': opts,
                                     'handler': self.handler,
                                     }

        mpconf = os.path.join(os.path.dirname(__file__), 'cpmodpy.conf')
        f = open(mpconf, 'wb')
        try:
            f.write(conf_data)
        finally:
            f.close()

        response = read_process(self.apache_path, '-k start -f %s' % mpconf)
        self.ready = True
        return response

    def stop(self):
        os.popen('apache -k stop')
        self.ready = False
<|endoftext|>


<commit_before>class InvalidMove(Exception):
    pass

class Board(object):

    def __init__(self):
        self._board = [None]*64
        self.setup()

    def setup(self):
        self[0, 0] = Rock()
        self[1, 0] = Knight()
        self[2, 0] = Bishop()
        self[3, 0] = Queen()
        self[4, 0] = King()
        self[5, 0] = Bishop()
        self[6, 0] = Knight()
        self[7, 0] = Rock()

        for i in range(8):
            self[i, 1] = Pawn()
            self[i, 6] = Pawn(black=True)

        self[0, 7] = Rock(black=True)
        self[1, 7] = Knight(black=True)
        self[2, 7] = Bishop(black=True)
        self[3, 7] = Queen(black=True)
        self[4, 7] = King(black=True)
        self[5, 7] = Bishop(black=True)
        self[6, 7] = Knight(black=True)
        self[7, 7] = Rock(black=True)

        self._white_to_move=True

    def __setitem__(self, key, value):
        """
        i, j = key
        i ... 0..7, it means a..h
        j ... 0..7, it means 1..8

        e.g. e4 is (4, 3)
        """
        i, j = key
        self._board[j*8+i] = value

    def __getitem__(self, key):
        i, j = key
        return self._board[j*8+i]

    def parse_move(self, move):
        def convert_field(field):
            if len(field) == 2:
                x = field[0]
                y = field[1]
                if x == "a": i = 0
                if x == "b": i = 1
                if x == "c": i = 2
                if x == "d": i = 3
                if x == "e": i = 4
                if x == "f": i = 5
                if x == "g": i = 6
                if x == "h": i = 7
                j = int(y)-1
                return i, j
            else:
                raise InvalidMove()
        if move[0] == "R":
            piece = Rock
        elif move[0] == "N":
            piece = Knight
        elif move[0] == "B":
            piece = Bishop
        elif move[0] == "Q":
            piece = Queen
        elif move[0] == "K":
            piece = King
        else:
            piece = Pawn
        if piece == Pawn:
            field = convert_field(move)
        else:
            field = convert_field(move[1:])
        capture = False
        check = False
        return piece, field, capture, check

    def find_piece(self, piece, field):
        """
        Finds the piece "piece" that can go to the field "field".
        """
        candidates = []
        # first find all pieces of the type "piece" on the board:
        for i in range(8):
            for j in range(8):
                if isinstance(self[i, j], piece) and \
                        (self[i, j].white() == self._white_to_move):
                            candidates += [(i, j)]
        # try each of them:
        candidates = [x for x in candidates if self[x].can_move(x, field)]
        return candidates

    def move_algebraic(self, move):
        """
        Do one move.

        "move" is given in the Short Algebraic notation.
        """
        if move == "O-O":
            # kingside castling
            if self._white_to_move:
                self.move_coordinate((4, 0), (6, 0))
            else:
                self.move_coordinate((4, 7), (6, 7))
        elif move == "O-O-O":
            # queenside castling
            if self._white_to_move:
                self.move_coordinate((4, 0), (2, 0))
            else:
                self.move_coordinate((4, 7), (2, 7))
        else:
            piece, field, capture, check = self.parse_move(move)
            possible_pieces = self.find_piece(piece, field)
            if len(possible_pieces) != 1:
                raise InvalidMove()
            self.move_coordinate(possible_pieces[0], field)

    def move_coordinate(self, old, new):
        """
        Do one move. "old" and "new" are coordinates.

        Example:
        >>> b.move_coordinate((0, 0), (4, 0))
        """

        p = self[old]
        if p is None:
            raise InvalidMove()

        if not (self._white_to_move == (not p.black())):
            raise InvalidMove()

        self[old] = None
        self[new] = p
        self._white_to_move = not self._white_to_move

    def to_ascii_art(self):
        s = ""
        s += "+" + "---+"*8 + "\n"
        for j in reversed(range(8)):
            row = "|"
            for i in range(8):
                if self[i, j] is not None:
                    if self[i, j].black():
                        row += "#%s#|" % self[i, j].to_ascii_art()
                    else:
                        row += " %s |" % self[i, j].to_ascii_art()
                else:
                    row += "   |"
            s += row + "\n"
            s += "+" + "---+"*8 + "\n"
        return s

    def __str__(self):
        return self.to_ascii_art()

class Piece(object):

    def __init__(self, black=False):
        self._black = black

    def black(self):
        return self._black

    def white(self):
        return not self._black

class Rock(Piece):

    def to_ascii_art(self):
        return "R"

class Knight(Piece):

    def to_ascii_art(self):
        return "N"

    def can_move(self, old, new):
        d = (old[0]-new[0])**2 + (old[1]-new[1])**2
        return d == 5

class Bishop(Piece):

    def to_ascii_art(self):
        return "B"

    def can_move(self, old, new):
        dx = old[0]-new[0]
        dy = old[1]-new[1]
        return (dx == dy) or (dx == -dy)

class Queen(Piece):

    def to_ascii_art(self):
        return "Q"

class King(Piece):

    def to_ascii_art(self):
        return "K"

class Pawn(Piece):

    def to_ascii_art(self):
        return "p"

    def can_move(self, old, new):
        dx = new[0]-old[0]
        dy = new[1]-old[1]
        if dx != 0:
            return False
        if self.white():
            return (dy == 1) or ((dy == 2) and (old[1] == 1))
        else:
            return (dy == -1) or ((dy == -2) and (old[1] == 6))

def main():
    b = Board()
    print b
    b.move_algebraic("e4")
    b.move_algebraic("e5")
    b.move_algebraic("Nf3")
    b.move_algebraic("Nc6")
    b.move_algebraic("Bb5")
    b.move_algebraic("a6")
    print b

if __name__ == "__main__":
    main()
<commit_msg>Capture implemented<commit_after>class InvalidMove(Exception):
    pass

class Board(object):

    def __init__(self):
        self._board = [None]*64
        self.setup()

    def setup(self):
        self[0, 0] = Rock()
        self[1, 0] = Knight()
        self[2, 0] = Bishop()
        self[3, 0] = Queen()
        self[4, 0] = King()
        self[5, 0] = Bishop()
        self[6, 0] = Knight()
        self[7, 0] = Rock()

        for i in range(8):
            self[i, 1] = Pawn()
            self[i, 6] = Pawn(black=True)

        self[0, 7] = Rock(black=True)
        self[1, 7] = Knight(black=True)
        self[2, 7] = Bishop(black=True)
        self[3, 7] = Queen(black=True)
        self[4, 7] = King(black=True)
        self[5, 7] = Bishop(black=True)
        self[6, 7] = Knight(black=True)
        self[7, 7] = Rock(black=True)

        self._white_to_move=True

    def __setitem__(self, key, value):
        """
        i, j = key
        i ... 0..7, it means a..h
        j ... 0..7, it means 1..8

        e.g. e4 is (4, 3)
        """
        i, j = key
        self._board[j*8+i] = value

    def __getitem__(self, key):
        i, j = key
        return self._board[j*8+i]

    def parse_move(self, move):
        def convert_field(field):
            if len(field) == 2:
                x = field[0]
                y = field[1]
                if x == "a": i = 0
                if x == "b": i = 1
                if x == "c": i = 2
                if x == "d": i = 3
                if x == "e": i = 4
                if x == "f": i = 5
                if x == "g": i = 6
                if x == "h": i = 7
                j = int(y)-1
                return i, j
            else:
                raise InvalidMove()
        if move[0] == "R":
            piece = Rock
        elif move[0] == "N":
            piece = Knight
        elif move[0] == "B":
            piece = Bishop
        elif move[0] == "Q":
            piece = Queen
        elif move[0] == "K":
            piece = King
        else:
            piece = Pawn
        if piece != Pawn:
            move = move[1:]
        if move[0] == "x":
            capture = True
            move = move[1:]
        else:
            capture = False
        field = convert_field(move)
        check = False
        return piece, field, capture, check

    def find_piece(self, piece, field):
        """
        Finds the piece "piece" that can go to the field "field".
        """
        candidates = []
        # first find all pieces of the type "piece" on the board:
        for i in range(8):
            for j in range(8):
                if isinstance(self[i, j], piece) and \
                        (self[i, j].white() == self._white_to_move):
                            candidates += [(i, j)]
        # try each of them:
        candidates = [x for x in candidates if self[x].can_move(x, field)]
        return candidates

    def move_algebraic(self, move):
        """
        Do one move.

        "move" is given in the Short Algebraic notation.
        """
        if move == "O-O":
            # kingside castling
            if self._white_to_move:
                self.move_coordinate((4, 0), (6, 0))
            else:
                self.move_coordinate((4, 7), (6, 7))
        elif move == "O-O-O":
            # queenside castling
            if self._white_to_move:
                self.move_coordinate((4, 0), (2, 0))
            else:
                self.move_coordinate((4, 7), (2, 7))
        else:
            piece, field, capture, check = self.parse_move(move)
            if capture:
                if self[field] is None:
                    raise InvalidMove()
            else:
                if self[field] is not None:
                    raise InvalidMove()
            possible_pieces = self.find_piece(piece, field)
            if len(possible_pieces) != 1:
                raise InvalidMove()
            self.move_coordinate(possible_pieces[0], field)

    def move_coordinate(self, old, new):
        """
        Do one move. "old" and "new" are coordinates.

        Example:
        >>> b.move_coordinate((0, 0), (4, 0))
        """

        p = self[old]
        if p is None:
            raise InvalidMove()

        if not (self._white_to_move == (not p.black())):
            raise InvalidMove()

        self[old] = None
        self[new] = p
        self._white_to_move = not self._white_to_move

    def to_ascii_art(self):
        s = ""
        s += "+" + "---+"*8 + "\n"
        for j in reversed(range(8)):
            row = "|"
            for i in range(8):
                if self[i, j] is not None:
                    if self[i, j].black():
                        row += "#%s#|" % self[i, j].to_ascii_art()
                    else:
                        row += " %s |" % self[i, j].to_ascii_art()
                else:
                    row += "   |"
            s += row + "\n"
            s += "+" + "---+"*8 + "\n"
        return s

    def __str__(self):
        return self.to_ascii_art()

class Piece(object):

    def __init__(self, black=False):
        self._black = black

    def black(self):
        return self._black

    def white(self):
        return not self._black

class Rock(Piece):

    def to_ascii_art(self):
        return "R"

class Knight(Piece):

    def to_ascii_art(self):
        return "N"

    def can_move(self, old, new):
        d = (old[0]-new[0])**2 + (old[1]-new[1])**2
        return d == 5

class Bishop(Piece):

    def to_ascii_art(self):
        return "B"

    def can_move(self, old, new):
        dx = old[0]-new[0]
        dy = old[1]-new[1]
        return (dx == dy) or (dx == -dy)

class Queen(Piece):

    def to_ascii_art(self):
        return "Q"

class King(Piece):

    def to_ascii_art(self):
        return "K"

class Pawn(Piece):

    def to_ascii_art(self):
        return "p"

    def can_move(self, old, new):
        dx = new[0]-old[0]
        dy = new[1]-old[1]
        if dx != 0:
            return False
        if self.white():
            return (dy == 1) or ((dy == 2) and (old[1] == 1))
        else:
            return (dy == -1) or ((dy == -2) and (old[1] == 6))

def main():
    b = Board()
    print b
    b.move_algebraic("e4")
    b.move_algebraic("e5")
    b.move_algebraic("Nf3")
    b.move_algebraic("Nc6")
    b.move_algebraic("Bb5")
    b.move_algebraic("a6")
    b.move_algebraic("Bxc6")
    print b

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


<commit_before>import os
from os.path import join

from spec import Spec, skip, eq_, ok_, raises

from invoke.vendor.etcaetera.adapter import File, Adapter

from invoke.config import Config
from invoke.exceptions import AmbiguousEnvVar, UncastableEnvVar

from _utils import CleanEnvSpec

def _loads_path(c, path):
    files = [x for x in c.config.adapters if isinstance(x, File)]
    paths = [x.filepath for x in files]
    found = any(x == path for x in paths)
    ok_(found, "{0!r} not found, file adapters: {1!r}".format(path, paths))

CONFIGS_PATH = join('tests', '_support', 'configs')

def _load(key, path):
    path = join(CONFIGS_PATH, key, path)
    c = Config(**{'{0}_prefix'.format(key): path})
    c.load()
    return c

class Config_(CleanEnvSpec):
    class init:
        "__init__"

        def can_be_empty(self):
            eq_(Config().__class__, Config) # derp

        def configure_global_location_prefix(self):
            # This is a bit funky but more useful than just replicating the
            # same test farther down?
            c = Config(global_prefix='meh')
            _loads_path(c, 'meh.yaml')

        def default_global_prefix_is_etc(self):
            # TODO: make this work on Windows somehow without being a total
            # tautology? heh.
            _loads_path(Config(), '/etc/invoke.yaml')

        def configure_user_location_prefix(self):
            c = Config(user_prefix='whatever')
            _loads_path(c, 'whatever.yaml')

        def default_user_prefix_is_homedir(self):
            _loads_path(Config(), '~/.invoke.yaml')

        def configure_project_location(self):
            c = Config(project_home='someproject')
            _loads_path(c, 'someproject/invoke.yaml')

        def configure_runtime_path(self):
            c = Config(runtime_path='some/path.yaml')
            _loads_path(c, 'some/path.yaml')

        def accepts_explicit_adapter_override_list(self):
            c = Config(adapters=[])
            # Slightly encapsulation-breaking. Meh.
            eq_(len(c.config.adapters), 0)

        def accepts_overrides_dict(self):
            c = Config(overrides={'I win': 'always'})
            c.load()
            eq_(c['I win'], 'always')

        def does_not_trigger_config_loading(self):
            # Cuz automatic loading could potentially be surprising.
            # Meh-tastic no-exception-raised test.
            class DummyAdapter(Adapter):
                def load(self, *args, **kwargs):
                    raise Exception("I shouldn't have been called!")
            c = Config(adapters=[DummyAdapter()])

    class basic_API:
        "Basic API components"

        class load:
            "load()"
            # TODO: rename?
            def can_be_called_empty(self):
                c = Config()
                c.load()
                eq_(c.keys(), [])

            def can_be_given_defaults_dict_arg(self):
                c = Config()
                c.load(defaults={'foo': 'bar'})
                eq_(c.foo, 'bar')

            def makes_data_available(self):
                c = Config(overrides={'foo': 'notbar'})
                ok_('foo' not in c.keys())
                c.load()
                ok_('foo' in c.keys())

        def allows_dict_and_attr_access(self):
            # TODO: combine with tests for Context probably
            c = Config({'foo': 'bar'})
            c.load()
            eq_(c.foo, 'bar')
            eq_(c['foo'], 'bar')

        def nested_dict_values_also_allow_dual_access(self):
            # TODO: ditto
            c = Config({'foo': 'bar', 'biz': {'baz': 'boz'}})
            c.load()
            # Sanity check - nested doesn't somehow kill simple top level
            eq_(c.foo, 'bar')
            eq_(c['foo'], 'bar')
            # Actual check
            eq_(c.biz.baz, 'boz')
            eq_(c['biz']['baz'], 'boz')
            eq_(c.biz['baz'], 'boz')
            eq_(c['biz'].baz, 'boz')

        def attr_access_has_useful_errr_msg(self):
            c = Config()
            c.load()
            try:
                c.nope
            except AttributeError as e:
                expected = """
No attribute or config key found for 'nope'

Valid real attributes: ['clone', 'from_data', 'load']

Valid keys: []""".lstrip()
                eq_(str(e), expected)
            else:
                assert False, "Didn't get an AttributeError on bad key!"

        def loaded_keys_are_not_case_munged(self):
            # Looks tautological, but ensures we're suppressing etcaetera's
            # default UPPERCASE_EVERYTHING behavior
            d = {'FOO': 'bar', 'biz': 'baz', 'Boz': 'buzz'}
            c = Config(d)
            c.load()
            for x in d:
                err = "Expected to find {0!r} in {1!r}, didn't"
                ok_(x in c, err.format(x, c.keys()))

        def is_iterable_like_dict(self):
            def expect(c, expected):
                eq_(set(c.keys()), expected)
                eq_(set(list(c)), expected)
            c = Config({'a': 1, 'b': 2})
            expect(c, set())
            c.load()
            expect(c, set(['a', 'b']))

        def supports_readonly_dict_protocols(self):
            # Use single-keypair dict to avoid sorting problems in tests.
            c = Config({'foo': 'bar'})
            c2 = Config({'foo': 'bar'})
            c.load()
            c2.load()
            ok_('foo' in c)
            eq_(c, c2)
            eq_(len(c), 1)
            eq_(c.get('foo'), 'bar')
            eq_(c.has_key('foo'), True)
            eq_(c.items(), [('foo', 'bar')])
            eq_(list(c.iteritems()), [('foo', 'bar')])
            eq_(list(c.iterkeys()), ['foo'])
            eq_(list(c.itervalues()), ['bar'])
            eq_(c.keys(), ['foo'])
            eq_(c.values(), ['bar'])

        def supports_mutation_dict_protocols(self):
            c = Config({'foo': 'bar'})
            c.load()
            eq_(c.pop('foo'), 'bar')
            eq_(len(c), 0)
            c.setdefault('biz', 'baz')
            eq_(c['biz'], 'baz')
            c['boz'] = 'buzz'
            eq_(len(c), 2)
            del c['boz']
            eq_(len(c), 1)
            ok_('boz' not in c)
            eq_(c.popitem(), ('biz', 'baz'))
            eq_(len(c), 0)
            c.update({'foo': 'bar'})
            eq_(c['foo'], 'bar')

        def string_display(self):
            "__str__ and friends"
            config = Config({'foo': 'bar'})
            config.load()
            eq_(str(config), "{'foo': 'bar'}")
            eq_(unicode(config), u"{'foo': 'bar'}")
            eq_(repr(config), "{'foo': 'bar'}")

    def python_modules_load_lowercase_but_not_special_vars(self):
        # Borrow another test's Python module.
        c = _load('global', join('python-only', 'invoke'))
        # Sanity test that lowercase works
        eq_(c.hooray, 'python')
        # Real test that builtins, etc are stripped out
        for special in ('builtins', 'file', 'package', 'name', 'doc'):
            ok_('__{0}__'.format(special) not in c)

    class system_global:
        "Systemwide conf file"
        def yaml_first(self):
            c = _load('global', join('yaml-only', 'invoke'))
            eq_(c.hooray, 'yaml')

        def json_if_no_yaml(self):
            c = _load('global', join('json-only', 'invoke'))
            eq_(c.hooray, 'json')

        def python_if_no_json_or_yaml(self):
            c = _load('global', join('python-only', 'invoke'))
            eq_(c.hooray, 'python')

    class user_specific:
        "User-specific conf file"
        def yaml_first(self):
            c = _load('user', join('yaml-only', '.invoke'))
            eq_(c.user, 'yaml')

        def json_if_no_yaml(self):
            c = _load('user', join('json-only', '.invoke'))
            eq_(c.user, 'json')

        def python_if_no_json_or_yaml(self):
            c = _load('user', join('python-only', '.invoke'))
            eq_(c.user, 'python')

    class project_specific:
        "Local-to-project conf file"
        def yaml_first(self):
            c = Config(project_home=join(CONFIGS_PATH, 'project', 'yaml-only'))
            c.load()
            eq_(c.project_setting, 'yaml')

        def json_if_no_yaml(self):
            c = Config(project_home=join(CONFIGS_PATH, 'project', 'json-only'))
            c.load()
            eq_(c.project_setting, 'json')

        def python_if_no_json_or_yaml(self):
            c = Config(project_home=join(CONFIGS_PATH, 'project', 'py-only'))
            c.load()
            eq_(c.project_setting, 'python')

        def loads_nothing_if_no_project_home_given(self):
            c = Config()
            c.load()
            eq_(c.keys(), [])

    def honors_conf_file_flag(self):
        c = Config(runtime_path=join(CONFIGS_PATH, 'runtime', 'runtime.yaml'))
        c.load()
        eq_(c["what time?"], "run time!")

    class env_vars:
        "Environment variables"
        def base_case(self):
            os.environ['FOO'] = 'bar'
            c = Config()
            c.load(defaults={'foo': 'notbar'})
            eq_(c.foo, 'bar')

        def non_predeclared_settings_do_not_get_consumed(self):
            os.environ['HELLO'] = "is it me you're looking for?"
            c = Config()
            c.load()
            ok_('HELLO' not in c)
            ok_('hello' not in c)

        def underscores_top_level(self):
            os.environ['FOO_BAR'] = 'biz'
            c = Config()
            c.load(defaults={'foo_bar': 'notbiz'})
            eq_(c.foo_bar, 'biz')

        def underscores_nested(self):
            os.environ['FOO_BAR'] = 'biz'
            c = Config()
            c.load(defaults={'foo': {'bar': 'notbiz'}})
            eq_(c.foo.bar, 'biz')

        def both_types_of_underscores_mixed(self):
            os.environ['FOO_BAR_BIZ'] = 'baz'
            c = Config()
            c.load(defaults={'foo_bar': {'biz': 'notbaz'}})
            eq_(c.foo_bar.biz, 'baz')

        @raises(AmbiguousEnvVar)
        def ambiguous_underscores_dont_guess(self):
            os.environ['FOO_BAR'] = 'biz'
            c = Config()
            c.load(defaults={'foo_bar': 'wat', 'foo': {'bar': 'huh'}})

        class type_casting:
            def strings_replaced_with_env_value(self):
                os.environ['FOO'] = u'myvalue'
                c = Config()
                c.load(defaults={'foo': 'myoldvalue'})
                eq_(c.foo, u'myvalue')
                ok_(isinstance(c.foo, unicode)) # FIXME: py3

            def unicode_replaced_with_env_value(self):
                os.environ['FOO'] = 'myunicode'
                c = Config()
                c.load(defaults={'foo': u'myoldvalue'})
                eq_(c.foo, 'myunicode')
                ok_(isinstance(c.foo, str)) # FIXME: py3

            def None_replaced(self):
                os.environ['FOO'] = 'something'
                c = Config()
                c.load(defaults={'foo': None})
                eq_(c.foo, 'something')

            def booleans(self):
                for input_, result in (
                    ('0', False),
                    ('1', True),
                    ('', False),
                    ('meh', True),
                    ('false', True),
                ):
                    os.environ['FOO'] = input_
                    c = Config()
                    c.load(defaults={'foo': bool()})
                    eq_(c.foo, result)

            def boolean_type_inputs_with_non_boolean_defaults(self):
                for input_ in ('0', '1', '', 'meh', 'false'):
                    os.environ['FOO'] = input_
                    c = Config()
                    c.load(defaults={'foo': 'bar'})
                    eq_(c.foo, input_)

            def numeric_types_become_casted(self):
                # int
                # float
                # long
                # ???
                skip()

            class uncastable_types:
                @raises(UncastableEnvVar)
                def _uncastable_type(self, default):
                    os.environ['FOO'] = 'stuff'
                    c = Config()
                    c.load(defaults={'foo': default})

                def lists(self):
                    self._uncastable_type(['a', 'list'])

                def tuples(self):
                    self._uncastable_type(('a', 'tuple'))

                def custom_types(self):
                    self._uncastable_type(object())

    class hierarchy:
        "Config hierarchy in effect"
        def systemwide_overrides_collection(self):
            skip()

        def user_overrides_systemwide(self):
            skip()

        def user_overrides_collection(self):
            skip()

        def project_overrides_user(self):
            skip()

        def project_overrides_systemwide(self):
            skip()

        def project_overrides_collection(self):
            skip()

        def env_vars_override_project(self):
            skip()

        def env_vars_override_user(self):
            skip()

        def env_vars_override_systemwide(self):
            skip()

        def env_vars_override_collection(self):
            skip()

        def runtime_overrides_env_vars(self):
            skip()

        def runtime_overrides_project(self):
            skip()

        def runtime_overrides_user(self):
            skip()

        def runtime_overrides_systemwide(self):
            skip()

        def runtime_overrides_collection(self):
            skip()

        def e_flag_overrides_all(self):
            "-e overrides run.echo for all other layers"
            skip()

    class clone:
        def setup(self):
            self.c = Config({'foo': {'bar': {'biz': ['baz']}}})

        def load_before_and_after(self):
            c = self.c
            c.load()
            c2 = c.clone()
            c2.load()
            eq_(c, c2)
            ok_(c is not c2)
            ok_(c.config is not c2.config)
            eq_(c.foo.bar.biz, c2.foo.bar.biz)
            ok_(c.foo.bar.biz is not c2.foo.bar.biz)

        def no_loading_at_all(self):
            c = self.c
            c2 = c.clone()
            eq_(c, c2)
            ok_(c is not c2)
            ok_(c.config is not c2.config)
            # Unloaded -> looks empty
            eq_(c.keys(), [])
            eq_(c2.keys(), [])

        def load_after_only(self):
            c = self.c
            c2 = c.clone()
            c2.load()
            eq_(c2.foo.bar.biz, ['baz'])
<commit_msg>Failing numeric types test<commit_after>import os
from os.path import join

from spec import Spec, skip, eq_, ok_, raises

from invoke.vendor.etcaetera.adapter import File, Adapter

from invoke.config import Config
from invoke.exceptions import AmbiguousEnvVar, UncastableEnvVar

from _utils import CleanEnvSpec

def _loads_path(c, path):
    files = [x for x in c.config.adapters if isinstance(x, File)]
    paths = [x.filepath for x in files]
    found = any(x == path for x in paths)
    ok_(found, "{0!r} not found, file adapters: {1!r}".format(path, paths))

CONFIGS_PATH = join('tests', '_support', 'configs')

def _load(key, path):
    path = join(CONFIGS_PATH, key, path)
    c = Config(**{'{0}_prefix'.format(key): path})
    c.load()
    return c

class Config_(CleanEnvSpec):
    class init:
        "__init__"

        def can_be_empty(self):
            eq_(Config().__class__, Config) # derp

        def configure_global_location_prefix(self):
            # This is a bit funky but more useful than just replicating the
            # same test farther down?
            c = Config(global_prefix='meh')
            _loads_path(c, 'meh.yaml')

        def default_global_prefix_is_etc(self):
            # TODO: make this work on Windows somehow without being a total
            # tautology? heh.
            _loads_path(Config(), '/etc/invoke.yaml')

        def configure_user_location_prefix(self):
            c = Config(user_prefix='whatever')
            _loads_path(c, 'whatever.yaml')

        def default_user_prefix_is_homedir(self):
            _loads_path(Config(), '~/.invoke.yaml')

        def configure_project_location(self):
            c = Config(project_home='someproject')
            _loads_path(c, 'someproject/invoke.yaml')

        def configure_runtime_path(self):
            c = Config(runtime_path='some/path.yaml')
            _loads_path(c, 'some/path.yaml')

        def accepts_explicit_adapter_override_list(self):
            c = Config(adapters=[])
            # Slightly encapsulation-breaking. Meh.
            eq_(len(c.config.adapters), 0)

        def accepts_overrides_dict(self):
            c = Config(overrides={'I win': 'always'})
            c.load()
            eq_(c['I win'], 'always')

        def does_not_trigger_config_loading(self):
            # Cuz automatic loading could potentially be surprising.
            # Meh-tastic no-exception-raised test.
            class DummyAdapter(Adapter):
                def load(self, *args, **kwargs):
                    raise Exception("I shouldn't have been called!")
            c = Config(adapters=[DummyAdapter()])

    class basic_API:
        "Basic API components"

        class load:
            "load()"
            # TODO: rename?
            def can_be_called_empty(self):
                c = Config()
                c.load()
                eq_(c.keys(), [])

            def can_be_given_defaults_dict_arg(self):
                c = Config()
                c.load(defaults={'foo': 'bar'})
                eq_(c.foo, 'bar')

            def makes_data_available(self):
                c = Config(overrides={'foo': 'notbar'})
                ok_('foo' not in c.keys())
                c.load()
                ok_('foo' in c.keys())

        def allows_dict_and_attr_access(self):
            # TODO: combine with tests for Context probably
            c = Config({'foo': 'bar'})
            c.load()
            eq_(c.foo, 'bar')
            eq_(c['foo'], 'bar')

        def nested_dict_values_also_allow_dual_access(self):
            # TODO: ditto
            c = Config({'foo': 'bar', 'biz': {'baz': 'boz'}})
            c.load()
            # Sanity check - nested doesn't somehow kill simple top level
            eq_(c.foo, 'bar')
            eq_(c['foo'], 'bar')
            # Actual check
            eq_(c.biz.baz, 'boz')
            eq_(c['biz']['baz'], 'boz')
            eq_(c.biz['baz'], 'boz')
            eq_(c['biz'].baz, 'boz')

        def attr_access_has_useful_errr_msg(self):
            c = Config()
            c.load()
            try:
                c.nope
            except AttributeError as e:
                expected = """
No attribute or config key found for 'nope'

Valid real attributes: ['clone', 'from_data', 'load']

Valid keys: []""".lstrip()
                eq_(str(e), expected)
            else:
                assert False, "Didn't get an AttributeError on bad key!"

        def loaded_keys_are_not_case_munged(self):
            # Looks tautological, but ensures we're suppressing etcaetera's
            # default UPPERCASE_EVERYTHING behavior
            d = {'FOO': 'bar', 'biz': 'baz', 'Boz': 'buzz'}
            c = Config(d)
            c.load()
            for x in d:
                err = "Expected to find {0!r} in {1!r}, didn't"
                ok_(x in c, err.format(x, c.keys()))

        def is_iterable_like_dict(self):
            def expect(c, expected):
                eq_(set(c.keys()), expected)
                eq_(set(list(c)), expected)
            c = Config({'a': 1, 'b': 2})
            expect(c, set())
            c.load()
            expect(c, set(['a', 'b']))

        def supports_readonly_dict_protocols(self):
            # Use single-keypair dict to avoid sorting problems in tests.
            c = Config({'foo': 'bar'})
            c2 = Config({'foo': 'bar'})
            c.load()
            c2.load()
            ok_('foo' in c)
            eq_(c, c2)
            eq_(len(c), 1)
            eq_(c.get('foo'), 'bar')
            eq_(c.has_key('foo'), True)
            eq_(c.items(), [('foo', 'bar')])
            eq_(list(c.iteritems()), [('foo', 'bar')])
            eq_(list(c.iterkeys()), ['foo'])
            eq_(list(c.itervalues()), ['bar'])
            eq_(c.keys(), ['foo'])
            eq_(c.values(), ['bar'])

        def supports_mutation_dict_protocols(self):
            c = Config({'foo': 'bar'})
            c.load()
            eq_(c.pop('foo'), 'bar')
            eq_(len(c), 0)
            c.setdefault('biz', 'baz')
            eq_(c['biz'], 'baz')
            c['boz'] = 'buzz'
            eq_(len(c), 2)
            del c['boz']
            eq_(len(c), 1)
            ok_('boz' not in c)
            eq_(c.popitem(), ('biz', 'baz'))
            eq_(len(c), 0)
            c.update({'foo': 'bar'})
            eq_(c['foo'], 'bar')

        def string_display(self):
            "__str__ and friends"
            config = Config({'foo': 'bar'})
            config.load()
            eq_(str(config), "{'foo': 'bar'}")
            eq_(unicode(config), u"{'foo': 'bar'}")
            eq_(repr(config), "{'foo': 'bar'}")

    def python_modules_load_lowercase_but_not_special_vars(self):
        # Borrow another test's Python module.
        c = _load('global', join('python-only', 'invoke'))
        # Sanity test that lowercase works
        eq_(c.hooray, 'python')
        # Real test that builtins, etc are stripped out
        for special in ('builtins', 'file', 'package', 'name', 'doc'):
            ok_('__{0}__'.format(special) not in c)

    class system_global:
        "Systemwide conf file"
        def yaml_first(self):
            c = _load('global', join('yaml-only', 'invoke'))
            eq_(c.hooray, 'yaml')

        def json_if_no_yaml(self):
            c = _load('global', join('json-only', 'invoke'))
            eq_(c.hooray, 'json')

        def python_if_no_json_or_yaml(self):
            c = _load('global', join('python-only', 'invoke'))
            eq_(c.hooray, 'python')

    class user_specific:
        "User-specific conf file"
        def yaml_first(self):
            c = _load('user', join('yaml-only', '.invoke'))
            eq_(c.user, 'yaml')

        def json_if_no_yaml(self):
            c = _load('user', join('json-only', '.invoke'))
            eq_(c.user, 'json')

        def python_if_no_json_or_yaml(self):
            c = _load('user', join('python-only', '.invoke'))
            eq_(c.user, 'python')

    class project_specific:
        "Local-to-project conf file"
        def yaml_first(self):
            c = Config(project_home=join(CONFIGS_PATH, 'project', 'yaml-only'))
            c.load()
            eq_(c.project_setting, 'yaml')

        def json_if_no_yaml(self):
            c = Config(project_home=join(CONFIGS_PATH, 'project', 'json-only'))
            c.load()
            eq_(c.project_setting, 'json')

        def python_if_no_json_or_yaml(self):
            c = Config(project_home=join(CONFIGS_PATH, 'project', 'py-only'))
            c.load()
            eq_(c.project_setting, 'python')

        def loads_nothing_if_no_project_home_given(self):
            c = Config()
            c.load()
            eq_(c.keys(), [])

    def honors_conf_file_flag(self):
        c = Config(runtime_path=join(CONFIGS_PATH, 'runtime', 'runtime.yaml'))
        c.load()
        eq_(c["what time?"], "run time!")

    class env_vars:
        "Environment variables"
        def base_case(self):
            os.environ['FOO'] = 'bar'
            c = Config()
            c.load(defaults={'foo': 'notbar'})
            eq_(c.foo, 'bar')

        def non_predeclared_settings_do_not_get_consumed(self):
            os.environ['HELLO'] = "is it me you're looking for?"
            c = Config()
            c.load()
            ok_('HELLO' not in c)
            ok_('hello' not in c)

        def underscores_top_level(self):
            os.environ['FOO_BAR'] = 'biz'
            c = Config()
            c.load(defaults={'foo_bar': 'notbiz'})
            eq_(c.foo_bar, 'biz')

        def underscores_nested(self):
            os.environ['FOO_BAR'] = 'biz'
            c = Config()
            c.load(defaults={'foo': {'bar': 'notbiz'}})
            eq_(c.foo.bar, 'biz')

        def both_types_of_underscores_mixed(self):
            os.environ['FOO_BAR_BIZ'] = 'baz'
            c = Config()
            c.load(defaults={'foo_bar': {'biz': 'notbaz'}})
            eq_(c.foo_bar.biz, 'baz')

        @raises(AmbiguousEnvVar)
        def ambiguous_underscores_dont_guess(self):
            os.environ['FOO_BAR'] = 'biz'
            c = Config()
            c.load(defaults={'foo_bar': 'wat', 'foo': {'bar': 'huh'}})

        class type_casting:
            def strings_replaced_with_env_value(self):
                os.environ['FOO'] = u'myvalue'
                c = Config()
                c.load(defaults={'foo': 'myoldvalue'})
                eq_(c.foo, u'myvalue')
                ok_(isinstance(c.foo, unicode)) # FIXME: py3

            def unicode_replaced_with_env_value(self):
                os.environ['FOO'] = 'myunicode'
                c = Config()
                c.load(defaults={'foo': u'myoldvalue'})
                eq_(c.foo, 'myunicode')
                ok_(isinstance(c.foo, str)) # FIXME: py3

            def None_replaced(self):
                os.environ['FOO'] = 'something'
                c = Config()
                c.load(defaults={'foo': None})
                eq_(c.foo, 'something')

            def booleans(self):
                for input_, result in (
                    ('0', False),
                    ('1', True),
                    ('', False),
                    ('meh', True),
                    ('false', True),
                ):
                    os.environ['FOO'] = input_
                    c = Config()
                    c.load(defaults={'foo': bool()})
                    eq_(c.foo, result)

            def boolean_type_inputs_with_non_boolean_defaults(self):
                for input_ in ('0', '1', '', 'meh', 'false'):
                    os.environ['FOO'] = input_
                    c = Config()
                    c.load(defaults={'foo': 'bar'})
                    eq_(c.foo, input_)

            def numeric_types_become_casted(self):
                for old, new_, result in (
                    (int, '5', 5),
                    (int, '5.0', 5),
                    (float, '5', 5.0),
                    (float, '5.5', 5.5),
                    (long, '5', 5L),
                    # TODO: more?
                ):
                    os.environ['FOO'] = new_
                    c = Config()
                    c.load(defaults={'foo': old()})
                    eq_(c.foo, result)

            class uncastable_types:
                @raises(UncastableEnvVar)
                def _uncastable_type(self, default):
                    os.environ['FOO'] = 'stuff'
                    c = Config()
                    c.load(defaults={'foo': default})

                def lists(self):
                    self._uncastable_type(['a', 'list'])

                def tuples(self):
                    self._uncastable_type(('a', 'tuple'))

                def custom_types(self):
                    self._uncastable_type(object())

    class hierarchy:
        "Config hierarchy in effect"
        def systemwide_overrides_collection(self):
            skip()

        def user_overrides_systemwide(self):
            skip()

        def user_overrides_collection(self):
            skip()

        def project_overrides_user(self):
            skip()

        def project_overrides_systemwide(self):
            skip()

        def project_overrides_collection(self):
            skip()

        def env_vars_override_project(self):
            skip()

        def env_vars_override_user(self):
            skip()

        def env_vars_override_systemwide(self):
            skip()

        def env_vars_override_collection(self):
            skip()

        def runtime_overrides_env_vars(self):
            skip()

        def runtime_overrides_project(self):
            skip()

        def runtime_overrides_user(self):
            skip()

        def runtime_overrides_systemwide(self):
            skip()

        def runtime_overrides_collection(self):
            skip()

        def e_flag_overrides_all(self):
            "-e overrides run.echo for all other layers"
            skip()

    class clone:
        def setup(self):
            self.c = Config({'foo': {'bar': {'biz': ['baz']}}})

        def load_before_and_after(self):
            c = self.c
            c.load()
            c2 = c.clone()
            c2.load()
            eq_(c, c2)
            ok_(c is not c2)
            ok_(c.config is not c2.config)
            eq_(c.foo.bar.biz, c2.foo.bar.biz)
            ok_(c.foo.bar.biz is not c2.foo.bar.biz)

        def no_loading_at_all(self):
            c = self.c
            c2 = c.clone()
            eq_(c, c2)
            ok_(c is not c2)
            ok_(c.config is not c2.config)
            # Unloaded -> looks empty
            eq_(c.keys(), [])
            eq_(c2.keys(), [])

        def load_after_only(self):
            c = self.c
            c2 = c.clone()
            c2.load()
            eq_(c2.foo.bar.biz, ['baz'])
<|endoftext|>