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

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

import rospy

from sensor_msgs.msg import Joy

class ButtonTransition(object):
    """
    Monitor button transitions.

    The transition is measured when read.
    """
    def __init__(self, val_func, down_val=True, up_val=False):
        self._raw_value = val_func
        self._down_val = down_val
        self._up_val = up_val
        self._up_checked = True
        self._down_checked = False

    def down(self):
        val = self._raw_value()
        if val == self._down_val:
            if not self._down_checked:
                self._down_checked = True
                return True
        else:
            self._down_checked = False
        return False

    def up(self):
        val = self._raw_value()
        if val == self._up_val:
            if not self._up_checked:
                self._up_checked = True
                return True
        else:
            self._up_checked = False
        return False

class StickTransition(object):
    """
    Monitor transitions in stick movement.

    The transition is measured when read.
    """
    def __init__(self, val_func, epsilon=0.05):
        self._raw_value = val_func
        self._epsilon = epsilon
        self._value = 0.0

    def value(self):
        self.changed()
        return self._value

    def changed(self):
        value = self._raw_value()
        if abs(value - self._value) > self._epsilon:
            self._value = value
            return True
        return False

    def increased(self):
        value = self._raw_value()
        if (value - self._value) > self._epsilon:
            self._value = value
            return True
        return False

    def decreased(self):
        value = self._raw_value()
        if (self._value - value) > self._epsilon:
            self._value = value
            return True
        return False

class Joystick(object):
    """
    Abstract base class to handle joystick input.
    """

    def __init__(self, scale=1.0, offset=0.0, deadband=0.1):
        """
        @param scale (float) - scaling applied to joystick values [1.0]
        @param offset (float) - joystick offset values, post-scaling [0.0]
        @param deadband (float) - deadband post scaling and offset [0.1]

        Maps joystick input to robot control.

        Raw joystick valuess are in [1.0...-1.0].
        """
        sub = rospy.Subscriber("/joy", Joy, self._on_joy)
        self._scale = scale
        self._offset = offset
        self._deadband = deadband
        self._controls = {}

        self._buttons = {}
        self._sticks = {}
        button_names = (
            'btnLeft', 'btnUp', 'btnDown', 'btnRight',
            'dPadUp', 'dPadDown', 'dPadLeft', 'dPadRight',
            'leftBumper', 'rightBumper',
            'leftTrigger', 'rightTrigger',
            'function1', 'function2')
        stick_names = (
            'leftStickHorz', 'leftStickVert',
            'rightStickHorz', 'rightStickVert')

        #doing this with lambda won't work
        def gen_val_func(name, type_name):
            def val_func():
                return type_name(
                    name in self._controls and self._controls[name])
            return val_func

        for name in button_names:
            self._buttons[name] = ButtonTransition(gen_val_func(name, bool))
        for name in stick_names:
            self._sticks[name] = StickTransition(gen_val_func(name, float))

    def _on_joy(self, msg):
        """ callback for messages from joystick input
        Args:
              msg(Joy): a joystick input message
        """
        raise NotImplementedError()

    def button_up(self, name):
        return self._buttons[name].up()

    def button_down(self, name):
        return self._buttons[name].down()

    def stick_changed(self, name):
        return self._sticks[name].changed()

    def stick_inc(self, name):
        return self._sticks[name].increased()

    def stick_dec(self, name):
        return self._sticks[name].decreased()

    def stick_value(self, name):
        """
        Returns:
            the deadbanded, scaled and offset value of the axis
        """
        value = self._sticks[name].value()
        if value > self._deadband or value < -self._deadband:
            return (value * self._scale) + self._offset
        return 0

class XboxController(Joystick):
    """
    Xbox specialization of Joystick.
    """
    def __init__(self, scale=1.0, offset=0.0, deadband=0.5):
        super(XboxController, self).__init__(scale, offset, deadband)

    def _on_joy(self, msg):
        """ callback for messages from joystick input
        Args:
              msg(Joy): a joystick input message
        """

        self._controls['btnLeft'] = (msg.buttons[2] == 1)
        self._controls['btnUp'] = (msg.buttons[3] == 1)
        self._controls['btnDown'] = (msg.buttons[0] == 1)
        self._controls['btnRight'] = (msg.buttons[1] == 1)

        self._controls['dPadUp'] = (msg.axes[7] > 0.5)
        self._controls['dPadDown'] = (msg.axes[7] < -0.5)
        self._controls['dPadLeft'] = (msg.axes[6] > 0.5)
        self._controls['dPadRight'] = (msg.axes[6] < -0.5)

        self._controls['leftStickHorz'] = msg.axes[0]
        self._controls['leftStickVert'] = msg.axes[1]
        self._controls['rightStickHorz'] = msg.axes[3]
        self._controls['rightStickVert'] = msg.axes[4]

        self._controls['leftBumper'] = (msg.buttons[4] == 1)
        self._controls['rightBumper'] = (msg.buttons[5] == 1)
        self._controls['leftTrigger'] = (msg.axes[2] < 0.0)
        self._controls['rightTrigger'] = (msg.axes[5] < 0.0)

        self._controls['function1'] = (msg.buttons[6] == 1)
        self._controls['function2'] = (msg.buttons[10] == 1)

class LogitechController(Joystick):
    """
    Logitech specialization of Joystick.
    """
    def __init__(self, scale=1.0, offset=0.0, deadband=0.1):
        super(LogitechController, self).__init__(scale, offset, deadband)

    def _on_joy(self, msg):
        """ callback for messages from joystick input
        Args:
              msg(Joy): a joystick input message
        """

        self._controls['btnLeft'] = (msg.buttons[0] == 1)
        self._controls['btnUp'] = (msg.buttons[3] == 1)
        self._controls['btnDown'] = (msg.buttons[1] == 1)
        self._controls['btnRight'] = (msg.buttons[2] == 1)

        self._controls['dPadUp'] = (msg.axes[5] > 0.5)
        self._controls['dPadDown'] = (msg.axes[5] < -0.5)
        self._controls['dPadLeft'] = (msg.axes[4] > 0.5)
        self._controls['dPadRight'] = (msg.axes[4] < -0.5)

        self._controls['leftStickHorz'] = msg.axes[0]
        self._controls['leftStickVert'] = msg.axes[1]
        self._controls['rightStickHorz'] = msg.axes[2]
        self._controls['rightStickVert'] = msg.axes[3]

        self._controls['leftBumper'] = (msg.buttons[4] == 1)
        self._controls['rightBumper'] = (msg.buttons[5] == 1)
        self._controls['leftTrigger'] = (msg.buttons[6] == 1)
        self._controls['rightTrigger'] = (msg.buttons[7] == 1)

        self._controls['function1'] = (msg.buttons[8] == 1)
        self._controls['function2'] = (msg.buttons[9] == 1)

class PS3Controller(Joystick):
    """
    PS3 specialization of Joystick.
    """
    def __init__(self, scale=1.0, offset=0.0, deadband=0.1):
        super(PS3Controller, self).__init__(scale, offset, deadband)

    def _on_joy(self, msg):
        """ callback for messages from joystick input
        Args:
              msg(Joy): a joystick input message
        """

        self._controls['btnLeft'] = (msg.buttons[15] == 1)
        self._controls['btnUp'] = (msg.buttons[12] == 1)
        self._controls['btnDown'] = (msg.buttons[14] == 1)
        self._controls['btnRight'] = (msg.buttons[13] == 1)

        self._controls['dPadUp'] = (msg.axes[4] > 0.5)
        self._controls['dPadDown'] = (msg.axes[6] < -0.5)
        self._controls['dPadLeft'] = (msg.axes[7] > 0.5)
        self._controls['dPadRight'] = (msg.axes[5] < -0.5)

        self._controls['leftStickHorz'] = msg.axes[0]
        self._controls['leftStickVert'] = msg.axes[1]
        self._controls['rightStickHorz'] = msg.axes[2]
        self._controls['rightStickVert'] = msg.axes[3]

        self._controls['leftBumper'] = (msg.buttons[10] == 1)
        self._controls['rightBumper'] = (msg.buttons[11] == 1)
        self._controls['leftTrigger'] = (msg.buttons[8] == 1)
        self._controls['rightTrigger'] = (msg.buttons[9] == 1)

        self._controls['function1'] = (msg.buttons[0] == 1)
        self._controls['function2'] = (msg.buttons[3] == 1)
<commit_msg>Fix D-Pad button mappings on ps3 joystick<commit_after>#!/usr/bin/env python

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

import rospy

from sensor_msgs.msg import Joy

class ButtonTransition(object):
    """
    Monitor button transitions.

    The transition is measured when read.
    """
    def __init__(self, val_func, down_val=True, up_val=False):
        self._raw_value = val_func
        self._down_val = down_val
        self._up_val = up_val
        self._up_checked = True
        self._down_checked = False

    def down(self):
        val = self._raw_value()
        if val == self._down_val:
            if not self._down_checked:
                self._down_checked = True
                return True
        else:
            self._down_checked = False
        return False

    def up(self):
        val = self._raw_value()
        if val == self._up_val:
            if not self._up_checked:
                self._up_checked = True
                return True
        else:
            self._up_checked = False
        return False

class StickTransition(object):
    """
    Monitor transitions in stick movement.

    The transition is measured when read.
    """
    def __init__(self, val_func, epsilon=0.05):
        self._raw_value = val_func
        self._epsilon = epsilon
        self._value = 0.0

    def value(self):
        self.changed()
        return self._value

    def changed(self):
        value = self._raw_value()
        if abs(value - self._value) > self._epsilon:
            self._value = value
            return True
        return False

    def increased(self):
        value = self._raw_value()
        if (value - self._value) > self._epsilon:
            self._value = value
            return True
        return False

    def decreased(self):
        value = self._raw_value()
        if (self._value - value) > self._epsilon:
            self._value = value
            return True
        return False

class Joystick(object):
    """
    Abstract base class to handle joystick input.
    """

    def __init__(self, scale=1.0, offset=0.0, deadband=0.1):
        """
        @param scale (float) - scaling applied to joystick values [1.0]
        @param offset (float) - joystick offset values, post-scaling [0.0]
        @param deadband (float) - deadband post scaling and offset [0.1]

        Maps joystick input to robot control.

        Raw joystick valuess are in [1.0...-1.0].
        """
        sub = rospy.Subscriber("/joy", Joy, self._on_joy)
        self._scale = scale
        self._offset = offset
        self._deadband = deadband
        self._controls = {}

        self._buttons = {}
        self._sticks = {}
        button_names = (
            'btnLeft', 'btnUp', 'btnDown', 'btnRight',
            'dPadUp', 'dPadDown', 'dPadLeft', 'dPadRight',
            'leftBumper', 'rightBumper',
            'leftTrigger', 'rightTrigger',
            'function1', 'function2')
        stick_names = (
            'leftStickHorz', 'leftStickVert',
            'rightStickHorz', 'rightStickVert')

        #doing this with lambda won't work
        def gen_val_func(name, type_name):
            def val_func():
                return type_name(
                    name in self._controls and self._controls[name])
            return val_func

        for name in button_names:
            self._buttons[name] = ButtonTransition(gen_val_func(name, bool))
        for name in stick_names:
            self._sticks[name] = StickTransition(gen_val_func(name, float))

    def _on_joy(self, msg):
        """ callback for messages from joystick input
        Args:
              msg(Joy): a joystick input message
        """
        raise NotImplementedError()

    def button_up(self, name):
        return self._buttons[name].up()

    def button_down(self, name):
        return self._buttons[name].down()

    def stick_changed(self, name):
        return self._sticks[name].changed()

    def stick_inc(self, name):
        return self._sticks[name].increased()

    def stick_dec(self, name):
        return self._sticks[name].decreased()

    def stick_value(self, name):
        """
        Returns:
            the deadbanded, scaled and offset value of the axis
        """
        value = self._sticks[name].value()
        if value > self._deadband or value < -self._deadband:
            return (value * self._scale) + self._offset
        return 0

class XboxController(Joystick):
    """
    Xbox specialization of Joystick.
    """
    def __init__(self, scale=1.0, offset=0.0, deadband=0.5):
        super(XboxController, self).__init__(scale, offset, deadband)

    def _on_joy(self, msg):
        """ callback for messages from joystick input
        Args:
              msg(Joy): a joystick input message
        """

        self._controls['btnLeft'] = (msg.buttons[2] == 1)
        self._controls['btnUp'] = (msg.buttons[3] == 1)
        self._controls['btnDown'] = (msg.buttons[0] == 1)
        self._controls['btnRight'] = (msg.buttons[1] == 1)

        self._controls['dPadUp'] = (msg.axes[7] > 0.5)
        self._controls['dPadDown'] = (msg.axes[7] < -0.5)
        self._controls['dPadLeft'] = (msg.axes[6] > 0.5)
        self._controls['dPadRight'] = (msg.axes[6] < -0.5)

        self._controls['leftStickHorz'] = msg.axes[0]
        self._controls['leftStickVert'] = msg.axes[1]
        self._controls['rightStickHorz'] = msg.axes[3]
        self._controls['rightStickVert'] = msg.axes[4]

        self._controls['leftBumper'] = (msg.buttons[4] == 1)
        self._controls['rightBumper'] = (msg.buttons[5] == 1)
        self._controls['leftTrigger'] = (msg.axes[2] < 0.0)
        self._controls['rightTrigger'] = (msg.axes[5] < 0.0)

        self._controls['function1'] = (msg.buttons[6] == 1)
        self._controls['function2'] = (msg.buttons[10] == 1)

class LogitechController(Joystick):
    """
    Logitech specialization of Joystick.
    """
    def __init__(self, scale=1.0, offset=0.0, deadband=0.1):
        super(LogitechController, self).__init__(scale, offset, deadband)

    def _on_joy(self, msg):
        """ callback for messages from joystick input
        Args:
              msg(Joy): a joystick input message
        """

        self._controls['btnLeft'] = (msg.buttons[0] == 1)
        self._controls['btnUp'] = (msg.buttons[3] == 1)
        self._controls['btnDown'] = (msg.buttons[1] == 1)
        self._controls['btnRight'] = (msg.buttons[2] == 1)

        self._controls['dPadUp'] = (msg.axes[5] > 0.5)
        self._controls['dPadDown'] = (msg.axes[5] < -0.5)
        self._controls['dPadLeft'] = (msg.axes[4] > 0.5)
        self._controls['dPadRight'] = (msg.axes[4] < -0.5)

        self._controls['leftStickHorz'] = msg.axes[0]
        self._controls['leftStickVert'] = msg.axes[1]
        self._controls['rightStickHorz'] = msg.axes[2]
        self._controls['rightStickVert'] = msg.axes[3]

        self._controls['leftBumper'] = (msg.buttons[4] == 1)
        self._controls['rightBumper'] = (msg.buttons[5] == 1)
        self._controls['leftTrigger'] = (msg.buttons[6] == 1)
        self._controls['rightTrigger'] = (msg.buttons[7] == 1)

        self._controls['function1'] = (msg.buttons[8] == 1)
        self._controls['function2'] = (msg.buttons[9] == 1)

class PS3Controller(Joystick):
    """
    PS3 specialization of Joystick.
    """
    def __init__(self, scale=1.0, offset=0.0, deadband=0.1):
        super(PS3Controller, self).__init__(scale, offset, deadband)

    def _on_joy(self, msg):
        """ callback for messages from joystick input
        Args:
              msg(Joy): a joystick input message
        """

        self._controls['btnLeft'] = (msg.buttons[15] == 1)
        self._controls['btnUp'] = (msg.buttons[12] == 1)
        self._controls['btnDown'] = (msg.buttons[14] == 1)
        self._controls['btnRight'] = (msg.buttons[13] == 1)

        self._controls['dPadUp'] = (msg.buttons[4] == 1)
        self._controls['dPadDown'] = (msg.buttons[6] == 1)
        self._controls['dPadLeft'] = (msg.buttons[7] == 1)
        self._controls['dPadRight'] = (msg.buttons[5] == 1)

        self._controls['leftStickHorz'] = msg.axes[0]
        self._controls['leftStickVert'] = msg.axes[1]
        self._controls['rightStickHorz'] = msg.axes[2]
        self._controls['rightStickVert'] = msg.axes[3]

        self._controls['leftBumper'] = (msg.buttons[10] == 1)
        self._controls['rightBumper'] = (msg.buttons[11] == 1)
        self._controls['leftTrigger'] = (msg.buttons[8] == 1)
        self._controls['rightTrigger'] = (msg.buttons[9] == 1)

        self._controls['function1'] = (msg.buttons[0] == 1)
        self._controls['function2'] = (msg.buttons[3] == 1)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*
from __future__ import absolute_import, unicode_literals

import warnings

from django.test import SimpleTestCase

from wagtail.utils.deprecation import RemovedInWagtail17Warning, SearchFieldsShouldBeAList

class TestThisShouldBeAList(SimpleTestCase):
    def test_add_a_list(self):
        with warnings.catch_warnings(record=True) as w:
            base = SearchFieldsShouldBeAList(['hello'])
            result = base + ['world']

            # Ensure that adding things together works
            self.assertEqual(result, ['hello', 'world'])
            # Ensure that a new SearchFieldsShouldBeAList was returned
            self.assertIsInstance(result, SearchFieldsShouldBeAList)
            # Check that no deprecation warnings were raised
            self.assertEqual(len(w), 0)

    def test_add_a_tuple(self):
        with warnings.catch_warnings(record=True) as w:
            base = SearchFieldsShouldBeAList(['hello'])
            result = base + ('world',)

            # Ensure that adding things together works
            self.assertEqual(result, ['hello', 'world'])
            # Ensure that a new SearchFieldsShouldBeAList was returned
            self.assertIsInstance(result, SearchFieldsShouldBeAList)
            # Check that a deprecation warning was raised
            self.assertEqual(len(w), 1)
            warning = w[0]
            self.assertIs(warning.category, RemovedInWagtail17Warning)
<commit_msg>Reset warnings before testing warnings<commit_after># -*- coding: utf-8 -*
from __future__ import absolute_import, unicode_literals

import warnings

from django.test import SimpleTestCase

from wagtail.utils.deprecation import RemovedInWagtail17Warning, SearchFieldsShouldBeAList

class TestThisShouldBeAList(SimpleTestCase):
    def test_add_a_list(self):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')

            base = SearchFieldsShouldBeAList(['hello'])
            result = base + ['world']

            # Ensure that adding things together works
            self.assertEqual(result, ['hello', 'world'])
            # Ensure that a new SearchFieldsShouldBeAList was returned
            self.assertIsInstance(result, SearchFieldsShouldBeAList)
            # Check that no deprecation warnings were raised
            self.assertEqual(len(w), 0)

    def test_add_a_tuple(self):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')

            base = SearchFieldsShouldBeAList(['hello'])
            result = base + ('world',)

            # Ensure that adding things together works
            self.assertEqual(result, ['hello', 'world'])
            # Ensure that a new SearchFieldsShouldBeAList was returned
            self.assertIsInstance(result, SearchFieldsShouldBeAList)
            # Check that a deprecation warning was raised
            self.assertEqual(len(w), 1)
            warning = w[0]
            self.assertIs(warning.category, RemovedInWagtail17Warning)
<|endoftext|>


<commit_before>"""This file is free software under the MIT license"""
from modloader import modinfo

class Mod(object):
    """The Mod class

    This is supposed to act like a superclass for mods.
    Execution order is as follows:
    :meth:`mod_load` -> :meth:`mod_complete`
    """
    def mod_info(self):
        """Get the mod info

        Mod class has to either override this function or contain class attributes `name`, `version`, `author` and optionally `nsfw`.

        Returns:
            A tuple with the name, version, author, and (optionally) if the mod is NSFW
        """
        return (self.name, self.version, self.author, self.nsfw if hasattr(self.__class__, "nsfw") else False)

    def mod_load(self):
        """Executes when the mod is loaded

        This is where you put patcher code.

        See Also:
            :meth:`mod_complete`
        """
        raise NotImplementedError("Mod load isn't overriden")

    def mod_complete(self):
        """Executes when all mods are loaded

        This method is useful for dependency loading but can be left empty otherwise
        """
        raise NotImplementedError("Mod complete isn't overriden")

def loadable_mod(modclass):
    """Annotation to add a Mod subclass to the mod list

    Args:
        modclass (Mod): The Mod class, not an instance of the Mod class

    Raises:
        Exception: If the given class is not a subclass of Mod
    """
    if not issubclass(modclass, Mod):
        raise Exception("Class must be a subclass of Mod")

    mod = modclass()

    modinfo.add_mod(mod.mod_info()[0], mod)
<commit_msg>Used getattr instead of hasattr<commit_after>"""This file is free software under the MIT license"""
from modloader import modinfo

class Mod(object):
    """The Mod class

    This is supposed to act like a superclass for mods.
    Execution order is as follows:
    :meth:`mod_load` -> :meth:`mod_complete`
    """
    def mod_info(self):
        """Get the mod info

        Mod class has to either override this function or contain class attributes `name`, `version`, `author` and optionally `nsfw`.

        Returns:
            A tuple with the name, version, author, and (optionally) if the mod is NSFW
        """
        return (self.name, self.version, self.author, getattr(self.__class__, "nsfw", False))

    def mod_load(self):
        """Executes when the mod is loaded

        This is where you put patcher code.

        See Also:
            :meth:`mod_complete`
        """
        raise NotImplementedError("Mod load isn't overriden")

    def mod_complete(self):
        """Executes when all mods are loaded

        This method is useful for dependency loading but can be left empty otherwise
        """
        raise NotImplementedError("Mod complete isn't overriden")

def loadable_mod(modclass):
    """Annotation to add a Mod subclass to the mod list

    Args:
        modclass (Mod): The Mod class, not an instance of the Mod class

    Raises:
        Exception: If the given class is not a subclass of Mod
    """
    if not issubclass(modclass, Mod):
        raise Exception("Class must be a subclass of Mod")

    mod = modclass()

    modinfo.add_mod(mod.mod_info()[0], mod)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
    wapiti.operations.models
    ~~~~~~~~~~~~~~~~~~~~~~~~

    This module provides structures and abstractions for creating
    consistent Operation interfaces, regardless of underlying
    Mediawiki API response types.

    For example the ``prop=revisions`` and ``list=usercontribs`` APIs
    both return lists of revision information, however not all of the
    attributes afforded by ``prop=revisions`` are available from
    ``list=usercontribs``. Wapiti models and operations strive to
    resolve and abstract this fact away from the user as sanely as
    possible.
"""
from __future__ import unicode_literals

from datetime import datetime
from collections import namedtuple

def parse_timestamp(timestamp):
    return datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%SZ')

LanguageLink = namedtuple('LanguageLink', 'url language origin_page')
InterwikiLink = namedtuple('InterwikiLink', 'url prefix origin_page')
ExternalLink = namedtuple('ExternalLink', 'url origin_page')

NamespaceDescriptor = namedtuple('NamespaceDescriptor', 'id title canonical')
InterwikiDescriptor = namedtuple('InterwikiDescriptor', 'alias url language')

WapitiModelAttr = namedtuple('WapitiModelAttr', 'name mw_name default display')

def title_talk2subject(title):
    talk_pref, _, title_suf = title.partition(':')
    subj_pref, _, _ = talk_pref.rpartition('talk')
    subj_pref = subj_pref.strip()
    new_title = subj_pref + ':' + title_suf
    new_title = new_title.lstrip(':')
    return new_title

def title_subject2talk(title):
    subj_pref, _, title_suf = title.partition(':')
    subj_pref = subj_pref.strip()
    if not subj_pref:
        talk_pref = 'Talk'
    elif subj_pref.endswith('talk'):
        talk_pref = subj_pref
    else:
        talk_pref = subj_pref + ' talk'
    new_title = talk_pref + ':' + title_suf
    return new_title

class WapitiModelMeta(type):
    """
    The foundation of Wapiti's data models, which attempt to add
    consistency and order to the wide variety of return types used
    across different Mediawiki APIs. This metaclass enables certain
    inheritance-like usage patterns in models. See WapitiModelBase's
    docstring for more information.

    The `attributes` dictionary is a mapping of Python class attribute
    names to Mediawiki API result keys (e.g., `pageid` becomes
    `page_id` on the Python object).

    The `defaults` dictionary is a mapping of Python attribute name to
    default value, if allowed. If an attribute does not have a default
    value, and is missing upon instantiation of a model, an exception
    will be raised.
    """
    attributes = {}
    defaults = {}

    def __new__(cls, name, bases, attrs):
        all_attributes = {}
        all_defaults = {}
        for base in bases:
            all_attributes.update(getattr(base, 'attributes', {}))
            all_defaults.update(getattr(base, 'defaults', {}))
        all_attributes.update(attrs.get('attributes', {}))
        all_defaults.update(attrs.get('defaults', {}))
        attrs['attributes'] = all_attributes
        attrs['defaults'] = all_defaults
        ret = super(WapitiModelMeta, cls).__new__(cls, name, bases, attrs)
        return ret

class WapitiModelBase(object):
    """
    The more-concrete counterpart of WapitiModelMeta, which primarily
    provides initialization logic.

    There are two methods for instantiation, the standard
    ``__init__()`` (e.g., ``CategoryInfo()``), which takes attributes
    as keyword arguments, and ``from_query()``, which usually takes a
    dictionary deserialized from JSON, as returned by Mediawiki
    API. For information on `attributes` and `defaults`, see
    WapitiModelMeta.
    """

    __metaclass__ = WapitiModelMeta
    attributes = {}
    defaults = {}

    def __init__(self, **kw):
        missing = []
        for m_attr_name in self.attributes:
            try:
                val = kw.pop(m_attr_name)
            except KeyError:
                try:
                    val = self.defaults[m_attr_name]
                except KeyError:
                    missing.append(m_attr_name)
                    continue
            setattr(self, m_attr_name, val)
        if missing:
            raise ValueError('missing expected keyword arguments: %r'
                             % missing)
        # TODO: raise on unexpected keyword arguments?
        return

    @classmethod
    def from_query(cls, q_dict, **kw):
        kwargs = {}
        all_q_dict = dict(kw)
        all_q_dict.update(q_dict)
        for m_attr_name, q_dict_key in cls.attributes.items():
            if q_dict_key is None:
                continue
            try:
                kwargs[m_attr_name] = all_q_dict[q_dict_key]
            except KeyError:
                pass
        return cls(**kwargs)

class PageIdentifier(WapitiModelBase):
    attributes = {'title': 'title',
                  'page_id': 'pageid',
                  'ns': 'ns',
                  'source': 'source'}
    defaults = {}

    @property
    def is_subject_page(self):
        return (self.ns >= 0 and self.ns % 2 == 0)

    @property
    def is_talk_page(self):
        return (self.ns >= 0 and self.ns % 2 == 1)

    def _to_string(self, raise_exc=False):
        try:
            class_name = self.__class__.__name__
            return (u'%s(%r, %r, %r, %r)'
                    % (class_name,
                       self.title,
                       self.page_id,
                       self.ns,
                       self.source))
        except AttributeError:
            if raise_exc:
                raise
            return super(PageIdentifier, self).__str__()

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

    def __repr__(self):
        try:
            return self._to_string(raise_exc=True)
        except:
            return super(PageIdentifier, self).__repr__()

class PageInfo(PageIdentifier):
    attributes = {'subject_id': 'subjectid',
                  'talk_id': 'talkid'}
    defaults = {'subject_id': None,
                'talk_id': None}

    def __init__(self, **kw):
        req_title = kw.pop('req_title', None)
        super(PageInfo, self).__init__(**kw)
        self.req_title = req_title or self.title

        if self.is_subject_page:
            self.subject_id = self.page_id
        elif self.is_talk_page:
            self.talk_id = self.page_id
        else:
            raise ValueError('special or nonexistent namespace: %r' % self.ns)

    def get_subject_info(self):
        if self.is_subject_page:
            return self
        if self.subject_id is None:
            raise ValueError('subject_id not set')
        subj_title = title_talk2subject(self.title)
        subj_ns = self.ns - 1
        kwargs = dict(self.__dict__)
        kwargs['title'] = subj_title
        kwargs['ns'] = subj_ns
        return PageInfo(**kwargs)

    def get_talk_info(self):
        if self.is_talk_page:
            return self
        if self.talk_id is None:
            raise ValueError('talk_id not set')
        talk_title = title_subject2talk(self.title)
        talk_ns = self.ns + 1
        kwargs = dict(self.__dict__)
        kwargs['title'] = talk_title
        kwargs['ns'] = talk_ns
        return PageInfo(**kwargs)

class CategoryInfo(PageInfo):
    attributes = {'total_count': 'size',
                  'page_count': 'pages',
                  'file_count': 'files',
                  'subcat_count': 'subcats'}
    defaults = {'total_count': 0,
                'page_count': 0,
                'file_count': 0,
                'subcat_count': 0}

class RevisionInfo(PageInfo):
    attributes = {'rev_id': 'revid',
                  'size': 'size',
                  'user_text': 'user',
                  'user_id': 'userid',
                  'timestamp': 'timestamp',
                  'comment': 'comment',
                  'parsed_comment': 'parsedcomment',
                  'tags': 'tags'}

    # note that certain revisions may have hidden the fields
    # user_id, user_text, and comment for administrative reasons,
    # aka "oversighting"
    # TODO: is oversighting better handled in operation?
    defaults = {'user_text': '!userhidden',
                'userid': -1,
                'comment': '',
                'parsed_comment': ''}

    def __init__(self, *a, **kw):
        super(RevisionInfo, self).__init__(*a, **kw)
        self.timestamp = parse_timestamp(self.timestamp)

class Revision(RevisionInfo):
    attributes = {'parent_rev_id': 'parentid',
                  'content': '*',
                  'is_parsed': 'is_parsed'}
    defaults = {'content': ''}  # necessary?

#
# Protections
#
NEW = 'NEW'
AUTOCONFIRMED = 'AUTOCONFIRMED'
SYSOP = 'SYSOP'
PROTECTION_ACTIONS = ('create', 'edit', 'move', 'upload')

Protection = namedtuple('Protection', 'level, expiry')

class ProtectionInfo(object):
    # TODO: turn into mixin, add to PageIdentifier
    """
    For more info on protection,
    see https://en.wikipedia.org/wiki/Wikipedia:Protection_policy
    """
    levels = {
        'new': NEW,
        'autoconfirmed': AUTOCONFIRMED,
        'sysop': SYSOP,
    }

    def __init__(self, protections, page_ident=None):
        self.page_ident = page_ident

        protections = protections or {}
        self.protections = {}
        for p in protections:
            if not p['expiry'] == 'infinity':
                expiry = parse_timestamp(p['expiry'])
            else:
                expiry = 'infinity'
            level = self.levels[p['level']]
            self.protections[p['type']] = Protection(level, expiry)

    @property
    def has_protection(self):
        return any([x.level != NEW for x in self.protections.values()])

    @property
    def has_indef(self):
        return any([x.expiry == 'infinity' for x in self.protections.values()])

    @property
    def is_full_prot(self):
        try:
            if self.protections['edit'].level == SYSOP and \
                    self.protections['move'].level == SYSOP:
                return True
            else:
                return False
        except (KeyError, AttributeError):
            return False

    @property
    def is_semi_prot(self):
        try:
            if self.protections['edit'].level == AUTOCONFIRMED:
                return True
            else:
                return False
        except (KeyError, AttributeError):
            return False

    def __repr__(self):
        return u'ProtectionInfo(%r)' % self.protections

class CoordinateIndentifier(object):
    def __init__(self, coord, page_ident=None):
        self.page_ident = page_ident
        self.lat = coord.get('lat')
        self.lon = coord.get('lon')
        self.type = coord.get('type')
        self.name = coord.get('name')
        self.dim = coord.get('dim')
        self.country = coord.get('country')
        self.region = coord.get('region')
        if coord.get('primary', False):
            self.primary = True
        else:
            self.primary = False
        return
<commit_msg>demonstrating fill<commit_after># -*- coding: utf-8 -*-
"""
    wapiti.operations.models
    ~~~~~~~~~~~~~~~~~~~~~~~~

    This module provides structures and abstractions for creating consistent
    Operation interfaces, regardless of underlying Mediawiki API response
    types.

    For example the ``prop=revisions`` and ``list=usercontribs`` APIs
    both return lists of revision information, however not all of the
    attributes afforded by ``prop=revisions`` are available from
    ``list=usercontribs``. Wapiti models and operations strive to
    resolve and abstract this fact away from the user as sanely as
    possible.
"""
from __future__ import unicode_literals

from datetime import datetime
from collections import namedtuple

def parse_timestamp(timestamp):
    return datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%SZ')

LanguageLink = namedtuple('LanguageLink', 'url language origin_page')
InterwikiLink = namedtuple('InterwikiLink', 'url prefix origin_page')
ExternalLink = namedtuple('ExternalLink', 'url origin_page')

NamespaceDescriptor = namedtuple('NamespaceDescriptor', 'id title canonical')
InterwikiDescriptor = namedtuple('InterwikiDescriptor', 'alias url language')

WapitiModelAttr = namedtuple('WapitiModelAttr', 'name mw_name default display')

def title_talk2subject(title):
    talk_pref, _, title_suf = title.partition(':')
    subj_pref, _, _ = talk_pref.rpartition('talk')
    subj_pref = subj_pref.strip()
    new_title = subj_pref + ':' + title_suf
    new_title = new_title.lstrip(':')
    return new_title

def title_subject2talk(title):
    subj_pref, _, title_suf = title.partition(':')
    subj_pref = subj_pref.strip()
    if not subj_pref:
        talk_pref = 'Talk'
    elif subj_pref.endswith('talk'):
        talk_pref = subj_pref
    else:
        talk_pref = subj_pref + ' talk'
    new_title = talk_pref + ':' + title_suf
    return new_title

class WapitiModelMeta(type):
    """
    The foundation of Wapiti's data models, which attempt to add
    consistency and order to the wide variety of return types used
    across different Mediawiki APIs. This metaclass enables certain
    inheritance-like usage patterns in models. See WapitiModelBase's
    docstring for more information.

    The `attributes` dictionary is a mapping of Python class attribute
    names to Mediawiki API result keys (e.g., `pageid` becomes
    `page_id` on the Python object).

    The `defaults` dictionary is a mapping of Python attribute name to
    default value, if allowed. If an attribute does not have a default
    value, and is missing upon instantiation of a model, an exception
    will be raised.
    """
    attributes = {}
    defaults = {}

    def __new__(cls, name, bases, attrs):
        all_attributes = {}
        all_defaults = {}
        for base in bases:
            all_attributes.update(getattr(base, 'attributes', {}))
            all_defaults.update(getattr(base, 'defaults', {}))
        all_attributes.update(attrs.get('attributes', {}))
        all_defaults.update(attrs.get('defaults', {}))
        attrs['attributes'] = all_attributes
        attrs['defaults'] = all_defaults
        ret = super(WapitiModelMeta, cls).__new__(cls, name, bases, attrs)
        return ret

class WapitiModelBase(object):
    """
    The more-concrete counterpart of WapitiModelMeta, which primarily
    provides initialization logic.

    There are two methods for instantiation, the standard
    ``__init__()`` (e.g., ``CategoryInfo()``), which takes attributes
    as keyword arguments, and ``from_query()``, which usually takes a
    dictionary deserialized from JSON, as returned by Mediawiki
    API. For information on `attributes` and `defaults`, see
    WapitiModelMeta.
    """

    __metaclass__ = WapitiModelMeta
    attributes = {}
    defaults = {}

    def __init__(self, **kw):
        missing = []
        for m_attr_name in self.attributes:
            try:
                val = kw.pop(m_attr_name)
            except KeyError:
                try:
                    val = self.defaults[m_attr_name]
                except KeyError:
                    missing.append(m_attr_name)
                    continue
            setattr(self, m_attr_name, val)
        if missing:
            raise ValueError('missing expected keyword arguments: %r'
                             % missing)
        # TODO: raise on unexpected keyword arguments?
        return

    @classmethod
    def from_query(cls, q_dict, **kw):
        kwargs = {}
        all_q_dict = dict(kw)
        all_q_dict.update(q_dict)
        for m_attr_name, q_dict_key in cls.attributes.items():
            if q_dict_key is None:
                continue
            try:
                kwargs[m_attr_name] = all_q_dict[q_dict_key]
            except KeyError:
                pass
        return cls(**kwargs)

class PageIdentifier(WapitiModelBase):
    attributes = {'title': 'title',
                  'page_id': 'pageid',
                  'ns': 'ns',
                  'source': 'source'}
    defaults = {}

    @property
    def is_subject_page(self):
        return (self.ns >= 0 and self.ns % 2 == 0)

    @property
    def is_talk_page(self):
        return (self.ns >= 0 and self.ns % 2 == 1)

    def _to_string(self, raise_exc=False):
        try:
            class_name = self.__class__.__name__
            return (u'%s(%r, %r, %r, %r)'
                    % (class_name,
                       self.title,
                       self.page_id,
                       self.ns,
                       self.source))
        except AttributeError:
            if raise_exc:
                raise
            return super(PageIdentifier, self).__str__()

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

    def __repr__(self):
        try:
            return self._to_string(raise_exc=True)
        except:
            return super(PageIdentifier, self).__repr__()

class PageInfo(PageIdentifier):
    attributes = {'subject_id': 'subjectid',
                  'talk_id': 'talkid'}
    defaults = {'subject_id': None,
                'talk_id': None}

    def __init__(self, **kw):
        req_title = kw.pop('req_title', None)
        super(PageInfo, self).__init__(**kw)
        self.req_title = req_title or self.title

        if self.is_subject_page:
            self.subject_id = self.page_id
        elif self.is_talk_page:
            self.talk_id = self.page_id
        else:
            raise ValueError('special or nonexistent namespace: %r' % self.ns)

    def get_subject_info(self):
        if self.is_subject_page:
            return self
        if self.subject_id is None:
            raise ValueError('subject_id not set')
        subj_title = title_talk2subject(self.title)
        subj_ns = self.ns - 1
        kwargs = dict(self.__dict__)
        kwargs['title'] = subj_title
        kwargs['ns'] = subj_ns
        return PageInfo(**kwargs)

    def get_talk_info(self):
        if self.is_talk_page:
            return self
        if self.talk_id is None:
            raise ValueError('talk_id not set')
        talk_title = title_subject2talk(self.title)
        talk_ns = self.ns + 1
        kwargs = dict(self.__dict__)
        kwargs['title'] = talk_title
        kwargs['ns'] = talk_ns
        return PageInfo(**kwargs)

class CategoryInfo(PageInfo):
    attributes = {'total_count': 'size',
                  'page_count': 'pages',
                  'file_count': 'files',
                  'subcat_count': 'subcats'}
    defaults = {'total_count': 0,
                'page_count': 0,
                'file_count': 0,
                'subcat_count': 0}

class RevisionInfo(PageInfo):
    attributes = {'rev_id': 'revid',
                  'size': 'size',
                  'user_text': 'user',
                  'user_id': 'userid',
                  'timestamp': 'timestamp',
                  'comment': 'comment',
                  'parsed_comment': 'parsedcomment',
                  'tags': 'tags'}

    # note that certain revisions may have hidden the fields
    # user_id, user_text, and comment for administrative reasons,
    # aka "oversighting"
    # TODO: is oversighting better handled in operation?
    defaults = {'user_text': '!userhidden',
                'userid': -1,
                'comment': '',
                'parsed_comment': ''}

    def __init__(self, *a, **kw):
        super(RevisionInfo, self).__init__(*a, **kw)
        self.timestamp = parse_timestamp(self.timestamp)

class Revision(RevisionInfo):
    attributes = {'parent_rev_id': 'parentid',
                  'content': '*',
                  'is_parsed': 'is_parsed'}
    defaults = {'content': ''}  # necessary?

#
# Protections
#
NEW = 'NEW'
AUTOCONFIRMED = 'AUTOCONFIRMED'
SYSOP = 'SYSOP'
PROTECTION_ACTIONS = ('create', 'edit', 'move', 'upload')

Protection = namedtuple('Protection', 'level, expiry')

class ProtectionInfo(object):
    # TODO: turn into mixin, add to PageIdentifier
    """
    For more info on protection,
    see https://en.wikipedia.org/wiki/Wikipedia:Protection_policy
    """
    levels = {
        'new': NEW,
        'autoconfirmed': AUTOCONFIRMED,
        'sysop': SYSOP,
    }

    def __init__(self, protections, page_ident=None):
        self.page_ident = page_ident

        protections = protections or {}
        self.protections = {}
        for p in protections:
            if not p['expiry'] == 'infinity':
                expiry = parse_timestamp(p['expiry'])
            else:
                expiry = 'infinity'
            level = self.levels[p['level']]
            self.protections[p['type']] = Protection(level, expiry)

    @property
    def has_protection(self):
        return any([x.level != NEW for x in self.protections.values()])

    @property
    def has_indef(self):
        return any([x.expiry == 'infinity' for x in self.protections.values()])

    @property
    def is_full_prot(self):
        try:
            if self.protections['edit'].level == SYSOP and \
                    self.protections['move'].level == SYSOP:
                return True
            else:
                return False
        except (KeyError, AttributeError):
            return False

    @property
    def is_semi_prot(self):
        try:
            if self.protections['edit'].level == AUTOCONFIRMED:
                return True
            else:
                return False
        except (KeyError, AttributeError):
            return False

    def __repr__(self):
        return u'ProtectionInfo(%r)' % self.protections

class CoordinateIndentifier(object):
    def __init__(self, coord, page_ident=None):
        self.page_ident = page_ident
        self.lat = coord.get('lat')
        self.lon = coord.get('lon')
        self.type = coord.get('type')
        self.name = coord.get('name')
        self.dim = coord.get('dim')
        self.country = coord.get('country')
        self.region = coord.get('region')
        if coord.get('primary', False):
            self.primary = True
        else:
            self.primary = False
        return
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygame
import random
from collections import deque, namedtuple
import os

# refer to Maze MASK constants for valid mask characters
sample_mask =\
    """

       #########################
       ##......................##
        ##......................##
         ##......................##
          ##........    ..........#
         ##......... ## .........##
        ##.......... ## ........##
       ##...........    .......##
       #......................##
       #......................#
       #......................#
       #......................#
       #......................#
       #......................#
       #......................#
       #......................#
       ########################

    """

bird =\
    """
                 ######
              ###......###
            ##............##
          ##...  ....  .....##
         #.....  ....  .......#
        #......................#
        #......................#
        #.......... ...........#
        #.........   ..........#
        #........     .........#
        #.......       ........#
        #........     .........#
        #.........   ..........#
        #.......... ...........#
        ##....................##
        ####................####
        ###..................###
        ###..................###
        ####................####
        ###..................###
        ##....................##         #
        ##....................##        ##
        #......................#       #.#
        #......................#     ##..#
        #......................#   ##...#
        #......................####....#
        #.............................#
        #............................#
        #..........................##
        #......................####
         #....................#
          ##................##
           #.#............#.#
           #.####......####.#
           #.#   ######   #.#
           #.#            #.#         ####
          ##.##          ##.##       #...#
         #.....#        #.....#     #....#
         #.#.#.#        #.#.#.#     #....#
         #.#.#.#        #.#.#.#     #....###
##########.#.#.##########.#.#.#######.......###
...............................................#
................................................#
###########################################......#
                                           ###....#
                                              ###..#
                                                 ###
    """

def main():
    os.environ['SDL_VIDEO_WINDOW_POS'] = '600, 30'
    pygame.init()
    # images and sizes
    image_dir = os.path.join(os.getcwd(), 'images')
    _path = pygame.image.load(os.path.join(image_dir, 'path.png'))
    wall = pygame.image.load(os.path.join(image_dir, 'wall.png'))
    indicator = pygame.image.load(os.path.join(image_dir, 'position.png'))
    entrance = pygame.image.load(os.path.join(image_dir, 'entrance.png'))
    _exit = pygame.image.load(os.path.join(image_dir, 'exit.png'))
    block_width, block_height = _path.get_size()
    # maze and position lists
    maze = Maze(bird)
    maze_text = str(maze)
    maze_rows = maze_text.splitlines(False)
    entrance_coordinates = list()
    exit_coordinates = list()
    path_coordinates = list()
    wall_coordinates = list()
    undefined_coordinates = list()
    lookup = {Maze.BLOCK_ENTRANCE: entrance_coordinates,
              Maze.BLOCK_EXIT: exit_coordinates,
              Maze.BLOCK_PATH: path_coordinates,
              Maze.BLOCK_WALL: wall_coordinates,
              Maze.BLOCK_UNDEFINED: undefined_coordinates}
    for row, row_string in enumerate(maze_rows):
        for col, char in enumerate(row_string):
            coordinates = (block_width * col, block_height * row)
            lookup[char].append(coordinates)
    path_coordinates.extend(entrance_coordinates)
    path_coordinates.extend(exit_coordinates)
    # display
    display_height = block_height * (len(maze_rows) + 1)
    display_width = block_width * (max(len(r) for r in maze_rows) + 1)
    display = pygame.display.set_mode((display_width, display_height))
    _path.convert_alpha()
    indicator.convert_alpha()
    entrance.convert_alpha()
    _exit.convert_alpha()
    # location
    loc = [entrance_coordinates[0][0], entrance_coordinates[0][1]]
    # main loop
    continue_main_loop = True
    while continue_main_loop:
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                continue_main_loop = False
            if event.type == pygame.KEYDOWN and event.key == pygame.K_UP:
                if (loc[0], loc[1] - block_height) in path_coordinates:
                    loc[1] -= block_height
            if event.type == pygame.KEYDOWN and event.key == pygame.K_DOWN:
                if (loc[0], loc[1] + block_height) in path_coordinates:
                    loc[1] += block_height
            if event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
                if (loc[0] + block_width, loc[1]) in path_coordinates:
                    loc[0] += block_width
            if event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT:
                if (loc[0] - block_width, loc[1]) in path_coordinates:
                    loc[0] -= block_width
        for coordinates in path_coordinates:
            display.blit(_path, coordinates)
        for coordinates in wall_coordinates:
            display.blit(wall, coordinates)
        display.blit(entrance, entrance_coordinates[0])
        display.blit(_exit, exit_coordinates[0])
        display.blit(indicator, loc)
        pygame.display.flip()

class Maze(object):
    Coordinates = namedtuple('Coordinates', ('row', 'col'))
    # text mask constants
    MASK_UNDEFINED = ' '
    MASK_USABLE = '.'
    MASK_UNUSABLE = '#'
    # status of block spaces
    BLOCK_UNDEFINED = ' '
    BLOCK_ENTRANCE = '@'
    BLOCK_EXIT = '!'
    BLOCK_PATH = '='
    BLOCK_WALL = '#'
    # neighbors definition
    BLOCK_NEIGHBORS = (Coordinates(-1, 0),  # up
                       Coordinates(1, 0),  # down
                       Coordinates(0, -1),  # left
                       Coordinates(0, 1))  # right

    def __init__(self, mask):
        """Create a random maze based on the text mask.

        mask: refer to MASK_* constants for characters to use in the mask
        """
        spaces = self._mask_to_spaces(mask)
        self._spaces = spaces
        self._spaces = self._generate_maze(spaces)

    def __str__(self):
        """Return a rectangular string representation of this maze."""
        # find the bounding rectangle
        rows, cols = zip(*self._spaces)
        row_min, row_max = min(rows), max(rows)
        col_min, col_max = min(cols), max(cols)
        # build the string
        rows_list = list()
        for row in range(row_min, row_max + 1):
            row_list = list()
            for col in range(col_min, col_max + 1):
                coordinates = self.Coordinates(row, col)
                try:
                    status = self._spaces[coordinates]
                except KeyError:
                    status = self.MASK_UNDEFINED
                #char = status_to_char[status]
                row_list.append(status)
            rows_list.append(row_list)
        return '\n'.join(''.join(char for char in row_list)
                         for row_list in rows_list)

    def _mask_to_spaces(self, mask):
        """Convert the provided mask string to a dict of usable/unusable spaces.

        mask: refer to MASK_* constants for characters to use in the mask
            unknown characters will be treated as unusable
        returns spaces: a dict with (row, col) keys and status values
        """
        valid_chars = (self.MASK_USABLE, self.MASK_UNUSABLE,
                       self.MASK_UNDEFINED)
        spaces = dict()
        for row, row_string in enumerate(mask.splitlines(False)):
            for col, char in enumerate(row_string):
                if char in valid_chars:
                    # ignore invalid chars
                    coordinates = self.Coordinates(row, col)
                    spaces[coordinates] = char
        return spaces

    def _generate_maze(self, spaces):
        """Modify dict of spaces into a maze and return it."""
        # find a position on an edge and start making the maze path
        edges = tuple(self._edges(spaces))  # efficient tester for spaces
        start = random.choice(edges)
        spaces[start] = self.BLOCK_ENTRANCE
        current_path = deque()
        current_path.append(start)
        potential_exit_and_length = (start, 1)  # longest path to an edge
        # iteratively loop until the algorithm can find no more usable spaces
        # current_path will get longer as it stretches through the maze
        # eventually it will get shorter and come back to zero as the maze
        # fills up
        space = start
        while current_path:
            neighbors = list(self._neighbors(space, spaces))
            random.shuffle(neighbors)
            for new_space, status in neighbors:
                if self._is_pathable(new_space, space, spaces):
                    # pathable neighbor ==> keep extending the path
                    spaces[new_space] = self.BLOCK_PATH  # set it as a path
                    current_path.append(new_space)  # extend the path stack
                    # track the best potential exit
                    new_length = len(current_path)
                    old_length = potential_exit_and_length[1]
                    if (new_length > old_length) and (new_space in edges):
                        potential_exit_and_length = (new_space, new_length)
                    # setup for the next iteration
                    space = new_space
                    break  # don't need to check more neighbors
            else:
                # no usable neighbors ==> back up one step
                try:
                    # normal case - back up one step
                    space = current_path.pop()
                except IndexError:
                    # special case - maze complete. no more usable spaces
                    pass
        # when done making the maze, mark all remaining usable spaces as walls
        for space, status in spaces.items():
            if status == self.MASK_USABLE:
                spaces[space] = self.BLOCK_WALL
        # finally mark longest path to edge as exit
        _exit = potential_exit_and_length[0]
        spaces[_exit] = self.BLOCK_EXIT
        return spaces

    def _edges(self, spaces):
        """Generate all edge coordinates in spaces.

        An edge is defined as a space that:
            1) is a user space
            AND
                a) has at least one unusable neighbor
                OR
                b) is a maximum or minimum usable row or column
        """
        # setup tracking and test variables
        edge_set = set()
        user_statuses = (self.BLOCK_EXIT, self.BLOCK_ENTRANCE, self.BLOCK_PATH,
                         self.MASK_USABLE)
        rows = [r for r, c in spaces]  # todo: there must be a better way
        cols = [c for r, c in spaces]
        row_minmax = min(rows), max(rows)
        col_minmax = min(cols), max(cols)
        for space, status in spaces.items():
            # usable or path spaces
            if status in user_statuses:
                for n, n_status in self._neighbors(space, spaces):
                    if n_status == self.MASK_UNUSABLE:
                        # at least one unusable neighbor
                        edge_set.add(space)
                        break  # no need to keep testing this space
                    else:
                        # OR min/max row/col
                        row, col = space
                        if row in row_minmax or col in col_minmax:
                            edge_set.add(space)
                            break  # no need to keep testing this space
        for edge in edge_set:
            yield edge

    def _neighbors(self, space, spaces):
        """Generate all neighbor coordinates and statuses of space."""
        row, col = space
        for row_delta, col_delta in self.BLOCK_NEIGHBORS:
            coordinates = self.Coordinates(row + row_delta, col + col_delta)
            try:
                status = spaces[coordinates]
                yield coordinates, status  # the coordinates exist
            except KeyError:
                pass  # the coordinates don't exist

    def _is_pathable(self, space, previous_space, spaces):
        """Return True if the space can be used as a path. False otherwise."""
        # for blocks, the rule is (usable) and (no path statuses)
        if spaces[space] != self.MASK_USABLE:
            return False
        path_statuses = (self.BLOCK_ENTRANCE, self.BLOCK_EXIT, self.BLOCK_PATH)
        for neighbor, status in self._neighbors(space, spaces):
            if neighbor == previous_space:
                continue  # skip the previous space
            if status in path_statuses:
                return False
        # if it couldn't be disqualified, allow it
        return True

if __name__ == '__main__':
    main()
<commit_msg>zip w/*<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygame
import random
from collections import deque, namedtuple
import os

# refer to Maze MASK constants for valid mask characters
sample_mask =\
    """

       #########################
       ##......................##
        ##......................##
         ##......................##
          ##........    ..........#
         ##......... ## .........##
        ##.......... ## ........##
       ##...........    .......##
       #......................##
       #......................#
       #......................#
       #......................#
       #......................#
       #......................#
       #......................#
       #......................#
       ########################

    """

bird =\
    """
                 ######
              ###......###
            ##............##
          ##...  ....  .....##
         #.....  ....  .......#
        #......................#
        #......................#
        #.......... ...........#
        #.........   ..........#
        #........     .........#
        #.......       ........#
        #........     .........#
        #.........   ..........#
        #.......... ...........#
        ##....................##
        ####................####
        ###..................###
        ###..................###
        ####................####
        ###..................###
        ##....................##         #
        ##....................##        ##
        #......................#       #.#
        #......................#     ##..#
        #......................#   ##...#
        #......................####....#
        #.............................#
        #............................#
        #..........................##
        #......................####
         #....................#
          ##................##
           #.#............#.#
           #.####......####.#
           #.#   ######   #.#
           #.#            #.#         ####
          ##.##          ##.##       #...#
         #.....#        #.....#     #....#
         #.#.#.#        #.#.#.#     #....#
         #.#.#.#        #.#.#.#     #....###
##########.#.#.##########.#.#.#######.......###
...............................................#
................................................#
###########################################......#
                                           ###....#
                                              ###..#
                                                 ###
    """

def main():
    os.environ['SDL_VIDEO_WINDOW_POS'] = '600, 30'
    pygame.init()
    # images and sizes
    image_dir = os.path.join(os.getcwd(), 'images')
    _path = pygame.image.load(os.path.join(image_dir, 'path.png'))
    wall = pygame.image.load(os.path.join(image_dir, 'wall.png'))
    indicator = pygame.image.load(os.path.join(image_dir, 'position.png'))
    entrance = pygame.image.load(os.path.join(image_dir, 'entrance.png'))
    _exit = pygame.image.load(os.path.join(image_dir, 'exit.png'))
    block_width, block_height = _path.get_size()
    # maze and position lists
    maze = Maze(bird)
    maze_text = str(maze)
    maze_rows = maze_text.splitlines(False)
    entrance_coordinates = list()
    exit_coordinates = list()
    path_coordinates = list()
    wall_coordinates = list()
    undefined_coordinates = list()
    lookup = {Maze.BLOCK_ENTRANCE: entrance_coordinates,
              Maze.BLOCK_EXIT: exit_coordinates,
              Maze.BLOCK_PATH: path_coordinates,
              Maze.BLOCK_WALL: wall_coordinates,
              Maze.BLOCK_UNDEFINED: undefined_coordinates}
    for row, row_string in enumerate(maze_rows):
        for col, char in enumerate(row_string):
            coordinates = (block_width * col, block_height * row)
            lookup[char].append(coordinates)
    path_coordinates.extend(entrance_coordinates)
    path_coordinates.extend(exit_coordinates)
    # display
    display_height = block_height * (len(maze_rows) + 1)
    display_width = block_width * (max(len(r) for r in maze_rows) + 1)
    display = pygame.display.set_mode((display_width, display_height))
    _path.convert_alpha()
    indicator.convert_alpha()
    entrance.convert_alpha()
    _exit.convert_alpha()
    # location
    loc = [entrance_coordinates[0][0], entrance_coordinates[0][1]]
    # main loop
    continue_main_loop = True
    while continue_main_loop:
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                continue_main_loop = False
            if event.type == pygame.KEYDOWN and event.key == pygame.K_UP:
                if (loc[0], loc[1] - block_height) in path_coordinates:
                    loc[1] -= block_height
            if event.type == pygame.KEYDOWN and event.key == pygame.K_DOWN:
                if (loc[0], loc[1] + block_height) in path_coordinates:
                    loc[1] += block_height
            if event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
                if (loc[0] + block_width, loc[1]) in path_coordinates:
                    loc[0] += block_width
            if event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT:
                if (loc[0] - block_width, loc[1]) in path_coordinates:
                    loc[0] -= block_width
        for coordinates in path_coordinates:
            display.blit(_path, coordinates)
        for coordinates in wall_coordinates:
            display.blit(wall, coordinates)
        display.blit(entrance, entrance_coordinates[0])
        display.blit(_exit, exit_coordinates[0])
        display.blit(indicator, loc)
        pygame.display.flip()

class Maze(object):
    Coordinates = namedtuple('Coordinates', ('row', 'col'))
    # text mask constants
    MASK_UNDEFINED = ' '
    MASK_USABLE = '.'
    MASK_UNUSABLE = '#'
    # status of block spaces
    BLOCK_UNDEFINED = ' '
    BLOCK_ENTRANCE = '@'
    BLOCK_EXIT = '!'
    BLOCK_PATH = '='
    BLOCK_WALL = '#'
    # neighbors definition
    BLOCK_NEIGHBORS = (Coordinates(-1, 0),  # up
                       Coordinates(1, 0),  # down
                       Coordinates(0, -1),  # left
                       Coordinates(0, 1))  # right

    def __init__(self, mask):
        """Create a random maze based on the text mask.

        mask: refer to MASK_* constants for characters to use in the mask
        """
        spaces = self._mask_to_spaces(mask)
        self._spaces = spaces
        self._spaces = self._generate_maze(spaces)

    def __str__(self):
        """Return a rectangular string representation of this maze."""
        # find the bounding rectangle
        rows, cols = zip(*self._spaces)
        row_min, row_max = min(rows), max(rows)
        col_min, col_max = min(cols), max(cols)
        # build the string
        rows_list = list()
        for row in range(row_min, row_max + 1):
            row_list = list()
            for col in range(col_min, col_max + 1):
                coordinates = self.Coordinates(row, col)
                try:
                    status = self._spaces[coordinates]
                except KeyError:
                    status = self.MASK_UNDEFINED
                #char = status_to_char[status]
                row_list.append(status)
            rows_list.append(row_list)
        return '\n'.join(''.join(char for char in row_list)
                         for row_list in rows_list)

    def _mask_to_spaces(self, mask):
        """Convert the provided mask string to a dict of usable/unusable spaces.

        mask: refer to MASK_* constants for characters to use in the mask
            unknown characters will be treated as unusable
        returns spaces: a dict with (row, col) keys and status values
        """
        valid_chars = (self.MASK_USABLE, self.MASK_UNUSABLE,
                       self.MASK_UNDEFINED)
        spaces = dict()
        for row, row_string in enumerate(mask.splitlines(False)):
            for col, char in enumerate(row_string):
                if char in valid_chars:
                    # ignore invalid chars
                    coordinates = self.Coordinates(row, col)
                    spaces[coordinates] = char
        return spaces

    def _generate_maze(self, spaces):
        """Modify dict of spaces into a maze and return it."""
        # find a position on an edge and start making the maze path
        edges = tuple(self._edges(spaces))  # efficient tester for spaces
        start = random.choice(edges)
        spaces[start] = self.BLOCK_ENTRANCE
        current_path = deque()
        current_path.append(start)
        potential_exit_and_length = (start, 1)  # longest path to an edge
        # iteratively loop until the algorithm can find no more usable spaces
        # current_path will get longer as it stretches through the maze
        # eventually it will get shorter and come back to zero as the maze
        # fills up
        space = start
        while current_path:
            neighbors = list(self._neighbors(space, spaces))
            random.shuffle(neighbors)
            for new_space, status in neighbors:
                if self._is_pathable(new_space, space, spaces):
                    # pathable neighbor ==> keep extending the path
                    spaces[new_space] = self.BLOCK_PATH  # set it as a path
                    current_path.append(new_space)  # extend the path stack
                    # track the best potential exit
                    new_length = len(current_path)
                    old_length = potential_exit_and_length[1]
                    if (new_length > old_length) and (new_space in edges):
                        potential_exit_and_length = (new_space, new_length)
                    # setup for the next iteration
                    space = new_space
                    break  # don't need to check more neighbors
            else:
                # no usable neighbors ==> back up one step
                try:
                    # normal case - back up one step
                    space = current_path.pop()
                except IndexError:
                    # special case - maze complete. no more usable spaces
                    pass
        # when done making the maze, mark all remaining usable spaces as walls
        for space, status in spaces.items():
            if status == self.MASK_USABLE:
                spaces[space] = self.BLOCK_WALL
        # finally mark longest path to edge as exit
        _exit = potential_exit_and_length[0]
        spaces[_exit] = self.BLOCK_EXIT
        return spaces

    def _edges(self, spaces):
        """Generate all edge coordinates in spaces.

        An edge is defined as a space that:
            1) is a user space
            AND
                a) has at least one unusable neighbor
                OR
                b) is a maximum or minimum usable row or column
        """
        # setup tracking and test variables
        edge_set = set()
        user_statuses = (self.BLOCK_EXIT, self.BLOCK_ENTRANCE, self.BLOCK_PATH,
                         self.MASK_USABLE)
        rows, cols = zip(*spaces)
        row_minmax = min(rows), max(rows)
        col_minmax = min(cols), max(cols)
        for space, status in spaces.items():
            # usable or path spaces
            if status in user_statuses:
                for n, n_status in self._neighbors(space, spaces):
                    if n_status == self.MASK_UNUSABLE:
                        # at least one unusable neighbor
                        edge_set.add(space)
                        break  # no need to keep testing this space
                    else:
                        # OR min/max row/col
                        row, col = space
                        if row in row_minmax or col in col_minmax:
                            edge_set.add(space)
                            break  # no need to keep testing this space
        for edge in edge_set:
            yield edge

    def _neighbors(self, space, spaces):
        """Generate all neighbor coordinates and statuses of space."""
        row, col = space
        for row_delta, col_delta in self.BLOCK_NEIGHBORS:
            coordinates = self.Coordinates(row + row_delta, col + col_delta)
            try:
                status = spaces[coordinates]
                yield coordinates, status  # the coordinates exist
            except KeyError:
                pass  # the coordinates don't exist

    def _is_pathable(self, space, previous_space, spaces):
        """Return True if the space can be used as a path. False otherwise."""
        # for blocks, the rule is (usable) and (no path statuses)
        if spaces[space] != self.MASK_USABLE:
            return False
        path_statuses = (self.BLOCK_ENTRANCE, self.BLOCK_EXIT, self.BLOCK_PATH)
        for neighbor, status in self._neighbors(space, spaces):
            if neighbor == previous_space:
                continue  # skip the previous space
            if status in path_statuses:
                return False
        # if it couldn't be disqualified, allow it
        return True

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


<commit_before>import numpy as np
from ..solution_array import SolutionArray
from string import Template
import itertools

try:
    from IPython.display import display, HTML
except ImportError:
    pass

from widget import widget

def showcadtoon(title, css=""):
    with open("%s.gpkit" % title, 'r') as f:
        css = "" % css
        display(HTML(f.read() + css))

def ractorpy(m, update_py, ranges, constraint_js="",
             showtables=["cost", "sensitivities"]):
    def ractivefn(sol):
        live = ""
        display(HTML(live))
        if showtables:
            print sol.table(showtables)
    return m.interact(ractivefn, ranges)

new_jswidget_id = itertools.count().next

def ractorjs(title, m, update_py, ranges, constraint_js=""):
    widget_id = "jswidget_"+str(new_jswidget_id())
    display(HTML("" %
                 (widget_id, constraint_js)))
    display(HTML("" % (widget_id, len(ranges), widget_id)))

    container_id = widget_id + "_container"
    display(HTML("

" % (container_id, container_id, container_id, container_id)))

    template_id = widget_id + "_template"
    template = '"
                     % (widget_id, varname, array, widget_id, bases[i])))
        template += ctrl_template.substitute(w=widget_id,
                                             var=("$%s$" % var),
                                             varname=varname,
                                             len=len(array)-1)
        data_init += "%s: %i, " % (varname, (len(array)-1)/2)
        i += 1

    evalarray = [""]*np.prod(lengths)

    m.substitutions.update(subs)
    sol = m.solve(verbosity=0, skipfailures=True)
    for j in range(len(sol)):
        solj = sol.atindex(j)
        soljv = solj["variables"]
        idxs = [subs[var][1].index(soljv[var]) for var in subs]
        k = sum(np.array(idxs) * np.array([1]+bases[:-1]))
        evalarray[k] = update_py(SolutionArray(solj))
    display(HTML("" % (widget_id, evalarray)))

    display(HTML(template + "
</script>"))

    loader = Template("""getScript('http://cdn.ractivejs.org/0.6.1/ractive.min.js', function() {
          $w.ractive = new Ractive({
          el: '$container_id',
          template: '#$template_id',
          magic: true,
          data: {$data_init},
          onchange: function() {
              var idxsum = 0
              for (var i=0; i<$w.n; i++) {
                  varname = 'var'+i
                  idx = $w.ractive.data[varname]
                  console.log("$w-"+varname);
                  document.getElementById("$w-"+varname).innerText = Math.round(100*$w.ranges[varname][idx])/100
                  idxsum += idx*$w.bases[i]
              }
              if ($w.storage[idxsum] === "") {
                $title.infeasibilitywarning = "Infeasible problem"
              } else {
                $title.infeasibilitywarning = ""
                eval($w.storage[idxsum] + $w.after)
              }
            }
        });

        $w.ractive.onchange()
})

MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
</script>""")

    display(HTML(""
        display(HTML(live))
        if showtables:
            print sol.table(showtables)
    return m.interact(ractivefn, ranges)

new_jswidget_id = itertools.count().next

def ractorjs(title, m, update_py, ranges, constraint_js=""):
    widget_id = "jswidget_"+str(new_jswidget_id())
    display(HTML("" %
                 (widget_id, constraint_js)))
    display(HTML("" % (widget_id, len(ranges), widget_id)))

    container_id = widget_id + "_container"
    display(HTML("

" % (container_id, container_id, container_id, container_id)))

    template_id = widget_id + "_template"
    template = '"
                     % (widget_id, varname, array, widget_id, bases[i])))
        template += ctrl_template.substitute(w=widget_id,
                                             var=("$%s$" % var),
                                             varname=varname,
                                             len=len(array)-1)
        data_init += "%s: %i, " % (varname, (len(array)-1)/2)
        i += 1

    evalarray = [""]*np.prod(lengths)

    m.substitutions.update(subs)
    sol = m.solve(verbosity=0, skipsweepfailures=True)
    for j in range(len(sol)):
        solj = sol.atindex(j)
        soljv = solj["variables"]
        idxs = [subs[var][1].index(soljv[var]) for var in subs]
        k = sum(np.array(idxs) * np.array([1]+bases[:-1]))
        evalarray[k] = update_py(SolutionArray(solj))
    display(HTML("" % (widget_id, evalarray)))

    display(HTML(template + "
</script>"))

    loader = Template("""getScript('http://cdn.ractivejs.org/0.6.1/ractive.min.js', function() {
          $w.ractive = new Ractive({
          el: '$container_id',
          template: '#$template_id',
          magic: true,
          data: {$data_init},
          onchange: function() {
              var idxsum = 0
              for (var i=0; i<$w.n; i++) {
                  varname = 'var'+i
                  idx = $w.ractive.data[varname]
                  console.log("$w-"+varname);
                  document.getElementById("$w-"+varname).innerText = Math.round(100*$w.ranges[varname][idx])/100
                  idxsum += idx*$w.bases[i]
              }
              if ($w.storage[idxsum] === "") {
                $title.infeasibilitywarning = "Infeasible problem"
              } else {
                $title.infeasibilitywarning = ""
                eval($w.storage[idxsum] + $w.after)
              }
            }
        });

        $w.ractive.onchange()
})

MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
</script>""")

    display(HTML("


<commit_before>import zope.component
import zope.interface
import z3c.form
from z3c.json.converter import JSONWriter
from Products.Five.browser import BrowserView
from utils import dict2dynatree
from utils import lookupVocabulary
import interfaces

from Products.ATVocabularyManager import NamedVocabulary

class FieldVocabDynatreeJsonView(BrowserView):

    def __call__(self):
        fieldname = self.request.get('fieldname')
        #field = self.context.schema[fieldname]
        # TODO: This is hardcoded, Incredibly stupid, needs to change.
        atv = NamedVocabulary('staralliance-productcategories')
        tree = atv.getVocabularyDict(self.context)
        selected = self.request.get('selected', []).split('|')
        return JSONWriter().write(dict2dynatree(tree, selected,
                                                True,
                                                False))

class DynatreeWidget(z3c.form.browser.text.TextWidget):
    """
    A text field widget with a dynatree javascript vocabulary to determine the
    value.
    """
    zope.interface.implementsOnly(interfaces.IDynatreeWidget)
    klass = u'dynatree-widget'
    selectMode = 1
    minExpandLevel = 0
    rootVisible = False
    autoCollapse = False
    leafsOnly = True
    showKey = False
    atvocabulary = None

    def dynatree_parameters(self):
        result = [('%s,%s' % (parameter, getattr(self, parameter)))
                  for parameter in ['selectMode',
                                    'minExpandLevel',
                                    'rootVisible',
                                    'autoCollapse']]
        result.append('title,%s' % self.label)
        return '/'.join(result)

    def dynatree_value(self, value):
        if isinstance(value, basestring):
            return value
        if isinstance(value, (tuple, list)):
            return '|'.join(value)
        return ''

@zope.component.adapter(zope.schema.TextLine, z3c.form.interfaces.IFormLayer)
@zope.interface.implementer(z3c.form.interfaces.IFieldWidget)
def DynatreeFieldWidget(field, request):
    """ IFieldWidget factory for DynatreeWidget
    """
    return z3c.form.widget.FieldWidget(field, DynatreeWidget(request))

<commit_msg>as long as we still hard-code the vocab-name, we might as well use the updated one<commit_after>import zope.component
import zope.interface
import z3c.form
from z3c.json.converter import JSONWriter
from Products.Five.browser import BrowserView
from utils import dict2dynatree
from utils import lookupVocabulary
import interfaces

from Products.ATVocabularyManager import NamedVocabulary

class FieldVocabDynatreeJsonView(BrowserView):

    def __call__(self):
        fieldname = self.request.get('fieldname')
        #field = self.context.schema[fieldname]
        # TODO: This is hardcoded, Incredibly stupid, needs to change.
        atv = NamedVocabulary('staralliance.productcategories')
        tree = atv.getVocabularyDict(self.context)
        selected = self.request.get('selected', []).split('|')
        return JSONWriter().write(dict2dynatree(tree, selected,
                                                True,
                                                False))

class DynatreeWidget(z3c.form.browser.text.TextWidget):
    """
    A text field widget with a dynatree javascript vocabulary to determine the
    value.
    """
    zope.interface.implementsOnly(interfaces.IDynatreeWidget)
    klass = u'dynatree-widget'
    selectMode = 1
    minExpandLevel = 0
    rootVisible = False
    autoCollapse = False
    leafsOnly = True
    showKey = False
    atvocabulary = None

    def dynatree_parameters(self):
        result = [('%s,%s' % (parameter, getattr(self, parameter)))
                  for parameter in ['selectMode',
                                    'minExpandLevel',
                                    'rootVisible',
                                    'autoCollapse']]
        result.append('title,%s' % self.label)
        return '/'.join(result)

    def dynatree_value(self, value):
        if isinstance(value, basestring):
            return value
        if isinstance(value, (tuple, list)):
            return '|'.join(value)
        return ''

@zope.component.adapter(zope.schema.TextLine, z3c.form.interfaces.IFormLayer)
@zope.interface.implementer(z3c.form.interfaces.IFieldWidget)
def DynatreeFieldWidget(field, request):
    """ IFieldWidget factory for DynatreeWidget
    """
    return z3c.form.widget.FieldWidget(field, DynatreeWidget(request))

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from __future__ import unicode_literals

"""
Fields, parameters, and coercion
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Fields are typed slots which normalize and validate values passed to
them, facilitating much more robust Operation usage.

Parameters (aka params) are the values passed to a field. Another
benefit of fields is that Operations will not initialize successfully
without proper parameters, allowing earlier error detection (and in
some cases correction).

Coercion refers a field's [limited] ability to transform certain
values into a parameters usable by the owning Operation. For instance,
to get all members of 'Category:Africa', one can use the GetCategory
operation like this, ``GetCategory('Category:Africa')``, or this,
``GetCategory('Africa')``. The ``query_field`` on the GetCategory
Operation will automatically prepend the 'Category:' prefix, as all
Wikipedia categories start with 'Category:'.

The normalized value can be retrieved with ``field.get_value()``,
which (currently) always returns a single string (as would be used in
an API call URL). ``field.get_value_list()`` also exists, which
returns a list of strings.

Fields also encapsulate a ``key``, which typically corresponds the URL
query argument name used in API request URLs, as well as key
preparation logic, like prefixing (e.g., 'title' -> 'gcmtitle').

Here are some notes on various aspects of coercion:

 - By default, fields allow submission of plain strings (or iterables
   of strings), which are then normalized and subject to cardinality
   constraints. This behavior can be disabled with allow_string=False.

 - Fields can also accept non-string objects (i.e., WapitiModel
   instances) by specifying the name of an attribute to get from any
   non-string value submitted to the field.
"""

from collections import Sequence, Set
from utils import is_scalar, prefixed

def param_list2str(p_list, prefix=None, keep_empty=False):
    if is_scalar(p_list):
        p_list = param_str2list(p_list, keep_empty)
    u_p_list = [unicode(p) for p in p_list]
    ret = "|".join([prefixed(t, prefix)
                    for t in u_p_list if (t or keep_empty)])
    return unicode(ret)

def param_str2list(p, keep_empty=False):
    p = p or ''
    if is_scalar(p):
        p = unicode(p)
    else:
        p = param_list2str(p)
    p_list = p.split('|')
    if not keep_empty:
        p_list = [sp for sp in p_list if sp]
    return p_list

def normalize_param(p, prefix=None, multi=None):
    p_list = param_str2list(p)
    if multi is False:
        if len(p_list) > 1:
            tmpl = 'expected singular query parameter, not %r'
            raise ValueError(tmpl % p)
    return param_list2str(p_list, prefix)

# unacceptablllllllle
PLURAL_MAP = {'titles': 'title', 'revids': 'revid'}

class Param(object):
    def __init__(self, key, default=None, val_prefix=None, **kw):
        if not key:
            raise ValueError('expected key, not %r' % key)
        self.key = unicode(key)
        self.val_prefix = val_prefix
        self.required = kw.pop('required', False)
        self.multi = kw.pop('multi', None)
        self.accept_str = kw.pop('accept_str', True)
        self.key_prefix = kw.pop('key_prefix', True)  # True = filled in later
        self.limit = kw.pop('limit', None)

        param_attr = kw.pop('attr', None)
        coerce_func = kw.pop('coerce', None)
        if coerce_func is None:
            if param_attr is None:
                param_attr = self.key
                if self.multi:
                    param_attr = PLURAL_MAP.get(param_attr, param_attr)
            if isinstance(param_attr, basestring):
                coerce_func = lambda x: getattr(x, param_attr)
            elif param_attr is None:
                coerce_func = lambda x: x
            else:
                raise TypeError("'attr' expected string")
        elif not callable(coerce_func):
            raise TypeError("'coerce' expected callable")
        self.coerce_func = coerce_func
        if kw:
            raise ValueError('unexpected keyword argument(s): %r' % kw)
        if default is not None:
            default = normalize_param(default, self.val_prefix, self.multi)
        self.default = default

    def get_key(self, key_prefix=None):
        if self.key_prefix:
            prefix = key_prefix
            if prefix is None:
                prefix = self.key_prefix
            if isinstance(prefix, basestring):
                prefix = unicode(prefix)
            else:
                raise TypeError('expected valid string prefix')
        else:
            prefix = ''
        return prefix + self.key

    def _coerce_value(self, value):
        # TODO: it's real late and this is a bit of a sty
        # also, in some cases the bar-split normalization
        # should not occur (e.g., on a URL)
        if not value or isinstance(value, basestring):
            return value
        try:
            return self.coerce_func(value)
        except AttributeError:
            pass

        if isinstance(value, (Sequence, Set)):
            # some models are iterable, but none are sequences/sets (yet)
            coerced = []
            for v in value:
                if isinstance(v, basestring):
                    coerced.append(v)
                else:
                    coerced.append(self.coerce_func(v))
            return coerced
        raise TypeError('could not coerce value %r to %r' % (value, self.key))

    def get_value(self, value, prefix=None):
        if prefix is None:
            prefix = self.val_prefix
        value = self._coerce_value(value)
        norm_val = normalize_param(value, prefix, self.multi)
        val = norm_val or self.default
        if val is None and self.required:
            raise ValueError('%r param is required' % self.key)
        return val

    def get_value_list(self, value, prefix=None):
        return param_str2list(self.get_value(value, prefix))

    def get_tuple(self):
        return (self.key, self.value)

    def get_tuple_from_kwargs(self, **kwargs):
        """
        Picks up appropriate values from kwargs,
        returns the defaults if nothing matches.
        """
        pass

    __call__ = get_value

class SingleParam(Param):
    def __init__(self, *a, **kw):
        kw['multi'] = False
        super(SingleParam, self).__init__(*a, **kw)

class MultiParam(Param):
    def __init__(self, *a, **kw):
        kw['multi'] = True
        super(MultiParam, self).__init__(*a, **kw)

class StaticParam(Param):
    def __init__(self, key, value):
        super(StaticParam, self).__init__(key, value)

    def get_key(self, *a, **kw):
        return self.key

    def get_value(self, *a, **kw):
        return self.default
<commit_msg>added PassthroughParam<commit_after># -*- coding: utf-8 -*-
from __future__ import unicode_literals

"""
Fields, parameters, and coercion
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Fields are typed slots which normalize and validate values passed to
them, facilitating much more robust Operation usage.

Parameters (aka params) are the values passed to a field. Another
benefit of fields is that Operations will not initialize successfully
without proper parameters, allowing earlier error detection (and in
some cases correction).

Coercion refers a field's [limited] ability to transform certain
values into a parameters usable by the owning Operation. For instance,
to get all members of 'Category:Africa', one can use the GetCategory
operation like this, ``GetCategory('Category:Africa')``, or this,
``GetCategory('Africa')``. The ``query_field`` on the GetCategory
Operation will automatically prepend the 'Category:' prefix, as all
Wikipedia categories start with 'Category:'.

The normalized value can be retrieved with ``field.get_value()``,
which (currently) always returns a single string (as would be used in
an API call URL). ``field.get_value_list()`` also exists, which
returns a list of strings.

Fields also encapsulate a ``key``, which typically corresponds the URL
query argument name used in API request URLs, as well as key
preparation logic, like prefixing (e.g., 'title' -> 'gcmtitle').

Here are some notes on various aspects of coercion:

 - By default, fields allow submission of plain strings (or iterables
   of strings), which are then normalized and subject to cardinality
   constraints. This behavior can be disabled with allow_string=False.

 - Fields can also accept non-string objects (i.e., WapitiModel
   instances) by specifying the name of an attribute to get from any
   non-string value submitted to the field.
"""

from collections import Sequence, Set
from utils import is_scalar, prefixed

def param_list2str(p_list, prefix=None, keep_empty=False):
    if is_scalar(p_list):
        p_list = param_str2list(p_list, keep_empty)
    u_p_list = [unicode(p) for p in p_list]
    ret = "|".join([prefixed(t, prefix)
                    for t in u_p_list if (t or keep_empty)])
    return unicode(ret)

def param_str2list(p, keep_empty=False):
    p = p or ''
    if is_scalar(p):
        p = unicode(p)
    else:
        p = param_list2str(p)
    p_list = p.split('|')
    if not keep_empty:
        p_list = [sp for sp in p_list if sp]
    return p_list

def normalize_param(p, prefix=None, multi=None):
    p_list = param_str2list(p)
    if multi is False:
        if len(p_list) > 1:
            tmpl = 'expected singular query parameter, not %r'
            raise ValueError(tmpl % p)
    return param_list2str(p_list, prefix)

# unacceptablllllllle
PLURAL_MAP = {'titles': 'title', 'revids': 'revid'}

class Param(object):
    def __init__(self, key, default=None, val_prefix=None, **kw):
        if not key:
            raise ValueError('expected key, not %r' % key)
        self.key = unicode(key)
        self.val_prefix = val_prefix
        self.required = kw.pop('required', False)
        self.multi = kw.pop('multi', None)
        self.accept_str = kw.pop('accept_str', True)
        self.key_prefix = kw.pop('key_prefix', True)  # True = filled in later
        self.limit = kw.pop('limit', None)

        param_attr = kw.pop('attr', None)
        coerce_func = kw.pop('coerce', None)
        if coerce_func is None:
            if param_attr is None:
                param_attr = self.key
                if self.multi:
                    param_attr = PLURAL_MAP.get(param_attr, param_attr)
            if isinstance(param_attr, basestring):
                coerce_func = lambda x: getattr(x, param_attr)
            elif param_attr is None:
                coerce_func = lambda x: x
            else:
                raise TypeError("'attr' expected string")
        elif not callable(coerce_func):
            raise TypeError("'coerce' expected callable")
        self.coerce_func = coerce_func
        if kw:
            raise ValueError('unexpected keyword argument(s): %r' % kw)
        if default is not None:
            default = normalize_param(default, self.val_prefix, self.multi)
        self.default = default

    def get_key(self, key_prefix=None):
        if self.key_prefix:
            prefix = key_prefix
            if prefix is None:
                prefix = self.key_prefix
            if isinstance(prefix, basestring):
                prefix = unicode(prefix)
            else:
                raise TypeError('expected valid string prefix')
        else:
            prefix = ''
        return prefix + self.key

    def _coerce_value(self, value):
        # TODO: it's real late and this is a bit of a sty
        # also, in some cases the bar-split normalization
        # should not occur (e.g., on a URL)
        if not value or isinstance(value, basestring):
            return value
        try:
            return self.coerce_func(value)
        except AttributeError:
            pass

        if isinstance(value, (Sequence, Set)):
            # some models are iterable, but none are sequences/sets (yet)
            coerced = []
            for v in value:
                if isinstance(v, basestring):
                    coerced.append(v)
                else:
                    coerced.append(self.coerce_func(v))
            return coerced
        raise TypeError('could not coerce value %r to %r' % (value, self.key))

    def get_value(self, value, prefix=None):
        if prefix is None:
            prefix = self.val_prefix
        value = self._coerce_value(value)
        norm_val = normalize_param(value, prefix, self.multi)
        val = norm_val or self.default
        if val is None and self.required:
            raise ValueError('%r param is required' % self.key)
        return val

    def get_value_list(self, value, prefix=None):
        return param_str2list(self.get_value(value, prefix))

    def get_tuple(self):
        return (self.key, self.value)

    def get_tuple_from_kwargs(self, **kwargs):
        """
        Picks up appropriate values from kwargs,
        returns the defaults if nothing matches.
        """
        pass

    __call__ = get_value

class SingleParam(Param):
    def __init__(self, *a, **kw):
        kw['multi'] = False
        super(SingleParam, self).__init__(*a, **kw)

class MultiParam(Param):
    def __init__(self, *a, **kw):
        kw['multi'] = True
        super(MultiParam, self).__init__(*a, **kw)

class StaticParam(Param):
    def __init__(self, key, value):
        super(StaticParam, self).__init__(key, value)

    def get_key(self, *a, **kw):
        return self.key

    def get_value(self, *a, **kw):
        return self.default

class PassthroughParam(Param):
    def __init__(self, *a, **kw):
        super(PassthroughParam, self).__init__(*a, **kw)

    def get_value(self, value, prefix=None):
        return value

    def get_value_list(self, value, prefix=None):
        if is_scalar(value):
            return [value]
        return value
<|endoftext|>