<commit_before>"""
This module contains mixins and classes for working with forms.
"""

from .base import RouteBase
from .template import TemplateRoute, XHRPartialRoute

class FormAdaptor(object):
    """
    This class servers as an adaptor for 3rd party form APIs that works with
    the default form-handling API in this module. It can also be used on its
    own as it provides basic means of performing validation of form data.

    The adaptor is instantiated with a dict-like object (usually
    :py:class:`~bottle.FormsDict`).
    """

    #: Mapping of fields names to validator functions. Validator functions are
    #: expected to take the field value as single positional argument, and
    #: return a boolean result of validation.
    validators = {}

    def __init__(self, data={}):
        self.data = data

    def is_valid(self):
        """
        Perform validation and return a boolean result.
        """
        for field, validator in self.validators.items():
            if not validator(self.data.get(field)):
                return False
        return True

class FormMixin(object):
    """
    Mixin that provides form-related functionality. The methods and properties
    in this class are implemented in a way that allows easy customization for
    any form API by focusing on the workflow rather than concreate APIs.
    """

    #: Form factory function or class. By default, the value of this property
    #: is used to construct form objects. Default form factory is
    #: :py:class:`~streamline.forms.FormAdaptor`.
    form_factory = FormAdaptor

    def get_form_factory(self):
        """
        Return form factory function/class. Default behvarior is to return the
        :py:attr:`~FormMixin.form_factory` property.
        """
        return self.form_factory

    def get_unbound_form(self):
        """
        Return unbound form object.
        """
        form_factory = self.get_form_factory()
        return form_factory()

    def get_bound_form(self):
        """
        Return bound form object.
        """
        form_factory = self.get_form_factory()
        return form_factory(self.request.forms)

    def get_form(self):
        """
        Return form object. Depending on the HTTP verb, this method returns
        either an unbound (GET) or a bound (all other verbs) form.
        """
        if hasattr(self, 'form'):
            return self.form
        if self.method == 'get':
            return self.get_unbound_form()
        return self.get_bound_form()

    def validate_form(self, form):
        """
        Perform validation and return the results of validation.
        """
        return form.is_valid()

    def show_form(self, *args, **kwargs):
        """
        Prepare for rendering a blank, unbound form.
        """
        return {}

    def validate(self, *args, **kwargs):
        """
        Branch to one of the outcome methods depending on form validation
        result. The business logic should be writen in the outcome methods
        :py:meth:`~form_valid` and :py:meth:`~form_invalid`.

        .. warning::
            An object that includes this mixin **must** set its ``form``
            property to a form object before this method is invoked. Failure to
            do this will result in ``AttributeError``. This is automatically
            handled in classes that include the
            :py:class:`~streamline.forms.FormBase` mixin.
        """
        if self.validate_form(self.form):
            return self.form_valid(*args, **kwargs)
        return self.form_invalid(*args, **kwargs)

    def form_valid(self, *args, **kwargs):
        """
        Handle positive form validation outcome.
        """
        pass

    def form_invalid(self, *args, **kwargs):
        """
        Handle negative form validation outcome.
        """
        pass

class FormBase(object):
    """
    Base mixin for form-related CBRH.
    """

    def get(self, *args, **kwargs):
        """
        Delegate to :py:meth:`~streamline.forms.FormMixin.show_form`.
        """
        return self.show_form(*args, **kwargs)

    def post(self, *args, **kwargs):
        """
        Delegate to :py:meth:`~streamline.forms.FormMixin.validate`.
        """
        return self.validate(*args, **kwargs)

class FormRoute(FormMixin, FormBase, RouteBase):
    """
    Class for form handling without templates.

    :subclasses: :py:class:`~streamline.base.RouteBase`
    :includes: :py:class:`~streamline.forms.FormMixin`,
               :py:class:`~streamline.forms.FormBase`
    """

    def create_response(self):
        self.form = self.get_form()
        super(FormRoute, self).create_response()

class TemplateFormRoute(FormMixin, FormBase, TemplateRoute):
    """
    Class for form handling with template rendering.

    :subclasses: :py:class:`~streamline.template.TemplateRoute`
    :includes: :py:class:`~streamline.forms.FormMixin`,
               :py:class:`~streamline.forms.FormBase`
    """

    def get_context(self):
        ctx = super(TemplateFormRoute, self).get_context()
        ctx['form'] = self.get_form()
        return ctx

    def create_response(self):
        self.form = self.get_form()
        super(TemplateFormRoute, self).create_response()

class XHRPartialFormRoute(FormMixin, FormBase, XHRPartialRoute):
    """
    Class for form handling with XHR partial rendering support.

    :subclasses: :py:class:`~streamline.template.XHRPartialRoute`
    :includes: :py:class:`~streamline.forms.FormMixin`
               :py:class:`~streamline.forms.FormBase`
    """

    def get_context(self):
        ctx = super(XHRPartialFormRoute, self).get_context()
        ctx['form'] = self.get_form()
        return ctx

    def create_response(self):
        self.form = self.get_form()
        super(XHRPartialFormRoute, self).create_response()

ROCAFormRoute = XHRPartialFormRoute
<commit_msg>Update docstrings as recommended in discussion<commit_after>"""
This module contains mixins and classes for working with forms.
"""

from .base import RouteBase
from .template import TemplateRoute, XHRPartialRoute

class FormAdaptor(object):
    """
    This class servers as an adaptor for 3rd party form APIs that works with
    the default form-handling API in this module. It can also be used on its
    own as it provides basic means of performing validation of form data.

    The adaptor is instantiated with a dict-like object (usually
    :py:class:`~bottle.FormsDict`).
    """

    #: Mapping of fields names to validator functions. Validator functions are
    #: expected to take the field value as single positional argument, and
    #: return a boolean result of validation.
    validators = {}

    def __init__(self, data={}):
        self.data = data

    def is_valid(self):
        """
        Perform validation and return a boolean result.
        """
        for field, validator in self.validators.items():
            if not validator(self.data.get(field)):
                return False
        return True

class FormMixin(object):
    """
    Mixin that provides form-related functionality. The methods and properties
    in this class are implemented in a way that allows easy customization for
    any form API by focusing on the workflow rather than concreate APIs.
    """

    #: Form factory function or class. By default, the value of this property
    #: is used to construct form objects. Default form factory is
    #: :py:class:`~streamline.forms.FormAdaptor`.
    form_factory = FormAdaptor

    def get_form_factory(self):
        """
        Return form factory function/class. Default behvarior is to return the
        :py:attr:`~FormMixin.form_factory` property.
        """
        return self.form_factory

    def get_unbound_form(self):
        """
        Return unbound form object.
        """
        form_factory = self.get_form_factory()
        return form_factory()

    def get_bound_form(self):
        """
        Return bound form object.
        """
        form_factory = self.get_form_factory()
        return form_factory(self.request.forms)

    def get_form(self):
        """
        Return form object. Depending on the HTTP verb, this method returns
        either an unbound (GET) or a bound (all other verbs) form. Once a form
        object is created, this method will keep returning the previously
        created instance.
        """
        if hasattr(self, 'form'):
            return self.form
        if self.method == 'get':
            return self.get_unbound_form()
        return self.get_bound_form()

    def validate_form(self, form):
        """
        Perform validation and return the results of validation.
        """
        return form.is_valid()

    def show_form(self, *args, **kwargs):
        """
        Prepare for rendering a blank, unbound form.
        """
        return {}

    def validate(self, *args, **kwargs):
        """
        Branch to one of the outcome methods depending on form validation
        result. The business logic should be writen in the outcome methods
        :py:meth:`~form_valid` and :py:meth:`~form_invalid`.

        .. warning::
            An object that includes this mixin **must** set its ``form``
            property to a form object before this method is invoked. Failure to
            do this will result in ``AttributeError``. This is automatically
            handled in classes that include the
            :py:class:`~streamline.forms.FormBase` mixin.
        """
        if self.validate_form(self.form):
            return self.form_valid(*args, **kwargs)
        return self.form_invalid(*args, **kwargs)

    def form_valid(self, *args, **kwargs):
        """
        Handle positive form validation outcome.
        """
        pass

    def form_invalid(self, *args, **kwargs):
        """
        Handle negative form validation outcome.
        """
        pass

class FormBase(object):
    """
    Base mixin for form-related CBRH.
    """

    def get(self, *args, **kwargs):
        """
        Delegate to :py:meth:`~streamline.forms.FormMixin.show_form`.
        """
        return self.show_form(*args, **kwargs)

    def post(self, *args, **kwargs):
        """
        Delegate to :py:meth:`~streamline.forms.FormMixin.validate`.
        """
        return self.validate(*args, **kwargs)

class FormRoute(FormMixin, FormBase, RouteBase):
    """
    Class for form handling without templates.

    :subclasses: :py:class:`~streamline.base.RouteBase`
    :includes: :py:class:`~streamline.forms.FormMixin`,
               :py:class:`~streamline.forms.FormBase`
    """

    def create_response(self):
        self.form = self.get_form()
        super(FormRoute, self).create_response()

class TemplateFormRoute(FormMixin, FormBase, TemplateRoute):
    """
    Class for form handling with template rendering.

    :subclasses: :py:class:`~streamline.template.TemplateRoute`
    :includes: :py:class:`~streamline.forms.FormMixin`,
               :py:class:`~streamline.forms.FormBase`
    """

    def get_context(self):
        ctx = super(TemplateFormRoute, self).get_context()
        ctx['form'] = self.get_form()
        return ctx

    def create_response(self):
        self.form = self.get_form()
        super(TemplateFormRoute, self).create_response()

class XHRPartialFormRoute(FormMixin, FormBase, XHRPartialRoute):
    """
    Class for form handling with XHR partial rendering support.

    :subclasses: :py:class:`~streamline.template.XHRPartialRoute`
    :includes: :py:class:`~streamline.forms.FormMixin`
               :py:class:`~streamline.forms.FormBase`
    """

    def get_context(self):
        ctx = super(XHRPartialFormRoute, self).get_context()
        ctx['form'] = self.get_form()
        return ctx

    def create_response(self):
        self.form = self.get_form()
        super(XHRPartialFormRoute, self).create_response()

ROCAFormRoute = XHRPartialFormRoute
<|endoftext|>


<commit_before>"""File for Batch managment classes"""
import yaml
import datetime
from collections import namedtuple

import os, sys
sys.path.append(os.path.abspath(".."))

from logobatch.batches import Batch

class BBatchFormatError(Exception):
    """Error thrown when yaml format is incorrect"""
    def __init__(self, message):
        super(BBatchFormatError, self).__init__(message)

class BatchManager:
    """
    The BatchManager class contains and manages all Batch objects

    BatchManager is responsible for:
       - parsing yaml files
       - creating Batch objects from yaml data
       - setting the base output path
       - building Batch objects
       - running Batch objects
    """

    def __init__(self, bbatch, outpath=None):
        """Initalizes all attributes """

        self.batches, self.addresses = self.parse_bbatch(bbatch)

    @staticmethod
    def parse_logobatch_config(config_yml=None, **kwds):
        """
        Parses config file for logobatch's main settings from a config file,
        typically found at: ~/.logobatch.yml
        INPUT:
            config_yml: path to the logobatch config file
            **kwds: raw dictionary
        OUTPUT: logobatch config data (compute/storage resources, email)
        """
        try:
            with open(config_yml, 'r') as yfile:
                kwds = yaml.load(yfile)
        except TypeError:
            pass

        compute = kwds.get('compute', None)
        if compute and not isinstance(compute, list):
            raise TypeError('Resources must be defined in YAML list
        storage = kwds.get('storage', None)
        default_email = kwds.get('default_email', None)

        LBConfigData = namedtuple('LBConfigData',
                                  ['compute', 'storage', 'default_email'])
        return LBConfigData(compute, storage, default_email)

    @staticmethod
    def parse_bbatch(bbatch):
        """ reads bbatch yaml file into batches """

        with open(bbatch, 'r') as yfile:
            ydata = yaml.load(yfile)

        batches = ydata.get('Batches', None)
        if not isinstance(batches, list):
            raise BBatchFormatError('batches must be in a list')
        batches = [Batch(**b) for b in batches if b.get('enabled', False)]
        addresses = ydata.get('Email', None).get('addresses', None)

        BBatchData = namedtuple('BBatchData', ['batches', 'addresses'])
        return BBatchData(batches, addresses)

    def launch_batches(self):
        """Triggers Batch objects to schedule their job files"""

        for batch in self.batches:
            batch.launch_batch()
<commit_msg>Fixed small error<commit_after>"""File for Batch managment classes"""
import yaml
import datetime
from collections import namedtuple

import os, sys
sys.path.append(os.path.abspath(".."))

from logobatch.batches import Batch

class BBatchFormatError(Exception):
    """Error thrown when yaml format is incorrect"""
    def __init__(self, message):
        super(BBatchFormatError, self).__init__(message)

class BatchManager:
    """
    The BatchManager class contains and manages all Batch objects

    BatchManager is responsible for:
       - parsing yaml files
       - creating Batch objects from yaml data
       - setting the base output path
       - building Batch objects
       - running Batch objects
    """
    #namedtuple('UnparsedData', ['name', 'variables'])
    #initial_raw_data =

    def __init__(self, bbatch, outpath=None):
        """Initalizes all attributes """

        self.batches, self.addresses = self.parse_bbatch(bbatch)

    @staticmethod
    def parse_logobatch_config(config_yml=None, **kwds):
        """
        Parses config file for logobatch's main settings from a config file,
        typically found at: ~/.logobatch.yml
        INPUT:
            config_yml: path to the logobatch config file
            **kwds: raw dictionary
        OUTPUT: logobatch config data (compute/storage resources, email)
        """
        try:
            with open(config_yml, 'r') as yfile:
                kwds = yaml.load(yfile)
        except TypeError:
            pass

        compute = kwds.get('compute', None)
        if compute and not isinstance(compute, list):
            raise TypeError('Resources must be defined in YAML list')
        storage = kwds.get('storage', None)
        default_email = kwds.get('default_email', None)

        LBConfigData = namedtuple('LBConfigData',
                                  ['compute', 'storage', 'default_email'])
        return LBConfigData(compute, storage, default_email)

    @staticmethod
    def parse_bbatch(bbatch):
        """ reads bbatch yaml file into batches """

        with open(bbatch, 'r') as yfile:
            ydata = yaml.load(yfile)

        batches = ydata.get('Batches', None)
        if not isinstance(batches, list):
            raise BBatchFormatError('batches must be in a list')
        batches = [Batch(**b) for b in batches if b.get('enabled', False)]
        addresses = ydata.get('Email', None).get('addresses', None)

        BBatchData = namedtuple('BBatchData', ['batches', 'addresses'])
        return BBatchData(batches, addresses)

    def launch_batches(self):
        """Triggers Batch objects to schedule their job files"""

        for batch in self.batches:
            batch.launch_batch()
<|endoftext|>


<commit_before>"""Base primititve classes for working with Storm."""
from __future__ import absolute_import, print_function, unicode_literals

from logging import Handler
from traceback import format_exc

from .ipc import send_message

# Support for Storm Log levels as per STORM-414
_STORM_LOG_TRACE = 0
_STORM_LOG_DEBUG = 1
_STORM_LOG_INFO = 2
_STORM_LOG_WARN = 3
_STORM_LOG_ERROR = 4
_STORM_LOG_LEVELS = {
    'trace': _STORM_LOG_TRACE,
    'debug': _STORM_LOG_DEBUG,
    'info': _STORM_LOG_INFO,
    'warn': _STORM_LOG_WARN,
    'error': _STORM_LOG_ERROR,
}

class StormHandler(Handler):
    """Handler that will send messages back to Storm."""

    def __init__(self):
        """ Initialize handler """
        Handler.__init__(self)

    def emit(self, record):
        """
        Emit a record.

        If a formatter is specified, it is used to format the record.
        If exception information is present, it is formatted using
        traceback.print_exception and sent to Storm.
        """
        try:
            msg = self.format(record)
            level = _STORM_LOG_LEVELS.get(record.levelname.upper(),
                                          _STORM_LOG_INFO)
            send_message({'command': 'log', 'msg': str(msg), 'level': level})
        except Exception:
            self.handleError(record)

class Component(object):
    """Base class for Spouts and Bolts which contains class methods for
    logging messages back to the Storm worker process."""

    def _setup_component(self, storm_conf, context):
        """Add helpful instance variables to component after initial handshake
        with Storm.
        """
        self._topology_name = storm_conf.get('topology.name', '')
        self._task_id = context.get('taskid', '')
        self._component_name = context.get('task->component', {})\
                                      .get(str(self._task_id), '')
        self._debug = storm_conf.get("topology.debug", False)
        self._storm_conf = storm_conf
        self._context = context

    def raise_exception(self, exception, tup=None):
        """Report an exception back to Storm via logging.

        :param exception: a Python exception.
        :param tup: a :class:`Tuple` object.
        """
        if tup:
            message = ('Python {exception_name} raised while processing tuple '
                       '{tup!r}\n{traceback}')
        else:
            message = 'Python {exception_name} raised\n{traceback}'
        message = message.format(exception_name=exception.__class__.__name__,
                                 tup=tup,
                                 traceback=format_exc())
        self.log(message, 'error')
        send_message({'command': 'sync'})  # sync up right away

    def log(self, message, level=None):
        """Log a message to Storm optionally providing a logging level.

        :param message: the log message to send to Storm.
        :type message: str
        :param level: the logging level that Storm should use when writing the
                      ``message``. Can be one of: trace, debug, info, warn, or
                      error (default: ``info``).
        :type level: str
        """
        level = _STORM_LOG_LEVELS.get(level, _STORM_LOG_INFO)
        send_message({'command': 'log', 'msg': str(message), 'level': level})
<commit_msg>Fix upper/lower mix-up<commit_after>"""Base primititve classes for working with Storm."""
from __future__ import absolute_import, print_function, unicode_literals

from logging import Handler
from traceback import format_exc

from .ipc import send_message

# Support for Storm Log levels as per STORM-414
_STORM_LOG_TRACE = 0
_STORM_LOG_DEBUG = 1
_STORM_LOG_INFO = 2
_STORM_LOG_WARN = 3
_STORM_LOG_ERROR = 4
_STORM_LOG_LEVELS = {
    'trace': _STORM_LOG_TRACE,
    'debug': _STORM_LOG_DEBUG,
    'info': _STORM_LOG_INFO,
    'warn': _STORM_LOG_WARN,
    'error': _STORM_LOG_ERROR,
}

class StormHandler(Handler):
    """Handler that will send messages back to Storm."""

    def __init__(self):
        """ Initialize handler """
        Handler.__init__(self)

    def emit(self, record):
        """
        Emit a record.

        If a formatter is specified, it is used to format the record.
        If exception information is present, it is formatted using
        traceback.print_exception and sent to Storm.
        """
        try:
            msg = self.format(record)
            level = _STORM_LOG_LEVELS.get(record.levelname.lower(),
                                          _STORM_LOG_INFO)
            send_message({'command': 'log', 'msg': str(msg), 'level': level})
        except Exception:
            self.handleError(record)

class Component(object):
    """Base class for Spouts and Bolts which contains class methods for
    logging messages back to the Storm worker process."""

    def _setup_component(self, storm_conf, context):
        """Add helpful instance variables to component after initial handshake
        with Storm.
        """
        self._topology_name = storm_conf.get('topology.name', '')
        self._task_id = context.get('taskid', '')
        self._component_name = context.get('task->component', {})\
                                      .get(str(self._task_id), '')
        self._debug = storm_conf.get("topology.debug", False)
        self._storm_conf = storm_conf
        self._context = context

    def raise_exception(self, exception, tup=None):
        """Report an exception back to Storm via logging.

        :param exception: a Python exception.
        :param tup: a :class:`Tuple` object.
        """
        if tup:
            message = ('Python {exception_name} raised while processing tuple '
                       '{tup!r}\n{traceback}')
        else:
            message = 'Python {exception_name} raised\n{traceback}'
        message = message.format(exception_name=exception.__class__.__name__,
                                 tup=tup,
                                 traceback=format_exc())
        self.log(message, 'error')
        send_message({'command': 'sync'})  # sync up right away

    def log(self, message, level=None):
        """Log a message to Storm optionally providing a logging level.

        :param message: the log message to send to Storm.
        :type message: str
        :param level: the logging level that Storm should use when writing the
                      ``message``. Can be one of: trace, debug, info, warn, or
                      error (default: ``info``).
        :type level: str
        """
        level = _STORM_LOG_LEVELS.get(level, _STORM_LOG_INFO)
        send_message({'command': 'log', 'msg': str(message), 'level': level})
<|endoftext|>


<commit_before>from twisted.internet import reactor, task, defer, threads
import heapq
import time
import copy
from twisted.internet.defer import DeferredQueue, DeferredLock
import weakref
import csv

class ProcessQueue():

	def __init__(self):
		self.queue = []
		self.consumers = []

		self.target = 0.5

		self.Kp = 3.0 #2
		self.Ki = 1.0 #1
		self.Kd = 10.0 #5

		self.drop = 0
		self.sum_Error = 0.0
		self.timings = 0.0
		self.prev_Error = 0.0

	def put(self, obj):
		insert_t = time.time()
		self.queue.append((insert_t, obj))

		self._process()

	def get(self):
		d = defer.Deferred()
		self.consumers += [d]

		reactor.callLater(0.0, self._process)
		return d

	def _process(self):
		try:
			while self.consumers != [] and self.queue != []:
				d = self.consumers.pop(0)
				obj = self.queue.pop(0)
				dt = threads.deferToThread(self._process_in_thread, d, obj)
		except Exception, e:
			print str(e)

	def _process_in_thread(self, d, obj):
		inserted_time, message = obj
		start_time = time.time()
		d.callback(message)
		end_time = time.time()
		print end_time - start_time

		self.timings = 0 * self.timings + 1 * (start_time - inserted_time)

		# the proportional term produces an output value that is proportional to the current error value
		P = self.timings - self.target

		# the contribution from the integral term is proportional to both the magnitude of the error and the duration of the error
		# I = 0.8 * self.sum_Error + 0.2 * P # the integral in a PID controller is the sum of the instantaneous error over time and gives the accumulated offset that should have been corrected previously
		I  = 0.95 * self.sum_Error + P

		# Derivative action predicts system behavior and thus improves settling time and stability of the system
		#D = P - I # the derivative of the process error is calculated by determining the slope of the error over time
		D = P - self.prev_Error

		self.prev_Error = P
		self.sum_Error = I

		self.drop = self.Kp*P + self.Ki*I + self.Kd*D
		save_drop = self.drop
		self.drop = max(0.0, self.drop)

		q_len = len(self.queue)
		del self.queue[:int(self.drop)]

		print "===== Delay: %.2f ==== Latency: %.2f ===== Estimate: %.2f =====" % (start_time - inserted_time, end_time - start_time, self.timings)
		print "====Before queue len: %.2f ==== Queue Len: %.2f ==== Drop Len: %.2f ======" % (q_len, len(self.queue), self.drop)

		with open('PIDcontrolVal.csv', 'ab') as outfile:
			csvW = csv.writer(outfile, delimiter=',')
			data = [[save_drop]]
			csvW.writerows(data)

		with open("PID.csv", "ab") as outfile:
			csvW = csv.writer(outfile, delimiter=',')
			pid = [[P, I, D]]
			csvW.writerows(pid)

		with open("queueLen.csv", "ab") as outfile:
			csvW = csv.writer(outfile, delimiter=',')
			qlen = [[q_len, len(self.queue)]]
			csvW.writerows(qlen)

		with open("delay.csv", "ab") as outfile:
			csvW = csv.writer(outfile, delimiter=',')
			vdelay = [[(start_time - inserted_time)]]
			csvW.writerows(vdelay)

<commit_msg>PQ process measure<commit_after>from twisted.internet import reactor, task, defer, threads
import heapq
import time
import copy
from twisted.internet.defer import DeferredQueue, DeferredLock
import weakref
import csv

class ProcessQueue():

	def __init__(self):
		self.queue = []
		self.consumers = []

		self.target = 0.5

		self.Kp = 3.0 #2
		self.Ki = 5.0 #1
		self.Kd = 10.0 #5

		self.drop = 0
		self.sum_Error = 0.0
		self.timings = 0.0
		self.prev_Error = 0.0

	def put(self, obj):
		insert_t = time.time()
		self.queue.append((insert_t, obj))

		self._process()

	def get(self):
		d = defer.Deferred()
		self.consumers += [d]

		reactor.callLater(0.0, self._process)
		return d

	def _process(self):
		try:
			while self.consumers != [] and self.queue != []:
				d = self.consumers.pop(0)
				obj = self.queue.pop(0)
				dt = threads.deferToThread(self._process_in_thread, d, obj)
		except Exception, e:
			print str(e)

	def _process_in_thread(self, d, obj):
		inserted_time, message = obj
		start_time = time.time()
		d.callback(message)
		end_time = time.time()
		print end_time - start_time

		self.timings = 0 * self.timings + 1 * (start_time - inserted_time)

		# the proportional term produces an output value that is proportional to the current error value
		P = self.timings - self.target

		# the contribution from the integral term is proportional to both the magnitude of the error and the duration of the error
		# I = 0.8 * self.sum_Error + 0.2 * P # the integral in a PID controller is the sum of the instantaneous error over time and gives the accumulated offset that should have been corrected previously
		I  = 0.95 * self.sum_Error + 0.2 * P

		# Derivative action predicts system behavior and thus improves settling time and stability of the system
		#D = P - I # the derivative of the process error is calculated by determining the slope of the error over time
		D = P - self.prev_Error

		self.prev_Error = P
		self.sum_Error = I

		self.drop = self.Kp*P + self.Ki*I + self.Kd*D
		save_drop = self.drop
		self.drop = max(0.0, self.drop)

		q_len = len(self.queue)
		del self.queue[:int(self.drop)]

		print "===== Delay: %.2f ==== Latency: %.2f ===== Estimate: %.2f =====" % (start_time - inserted_time, end_time - start_time, self.timings)
		print "====Before queue len: %.2f ==== Queue Len: %.2f ==== Drop Len: %.2f ======" % (q_len, len(self.queue), self.drop)

		with open('PIDcontrolVal.csv', 'ab') as outfile:
			csvW = csv.writer(outfile, delimiter=',')
			data = [[save_drop]]
			csvW.writerows(data)

		with open("PID.csv", "ab") as outfile:
			csvW = csv.writer(outfile, delimiter=',')
			pid = [[P, I, D]]
			csvW.writerows(pid)

		with open("queueLen.csv", "ab") as outfile:
			csvW = csv.writer(outfile, delimiter=',')
			qlen = [[q_len, len(self.queue)]]
			csvW.writerows(qlen)

		with open("delay.csv", "ab") as outfile:
			csvW = csv.writer(outfile, delimiter=',')
			vdelay = [[(start_time - inserted_time)]]
			csvW.writerows(vdelay)

<|endoftext|>


<commit_before>#!/usr/bin/env python
#-------------------------------------------------------------------------------------------------------
# Copyright (C) Microsoft. All rights reserved.
# Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
#-------------------------------------------------------------------------------------------------------

 # py 2.5
import sys
import os

def print_usage():
    print("jstoc tool")
    print("creates a (const char array) from a javascript file.")
    print("")
    print("usage: jstoc.py <js file path> <variable name>")
    sys.exit(1)

def convert():
    if len(sys.argv) < 3:
        print_usage()

    js_file_name = sys.argv[1]
    if os.path.isfile(js_file_name) == False:
        print_usage()

    h_file_name = js_file_name + '.h'

    js_file_time = os.path.getmtime(js_file_name)
    h_file_time = 0
    if os.path.isfile(h_file_name):
        h_file_time = os.path.getmtime(h_file_name)

    str_header = "static const char " + sys.argv[2] + "[] = {\n"

    # if header file is up to date and no update to jstoc.py since, skip..
    if h_file_time < js_file_time or h_file_time < os.path.getmtime(sys.argv[0]):
        with open(js_file_name, "rb") as js_file:
            last_break = len(str_header) # skip first line
            byte = js_file.read(1)
            while byte:
                str_header += str(ord(byte))
                str_header += ","
                # beautify a bit. limit column to ~80
                column_check = (len(str_header) + 1) - last_break
                if column_check > 5 and column_check % 80 < 5:
                    last_break = len(str_header) + 1
                    str_header += "\n"
                else:
                    str_header += " "
                byte = js_file.read(1)
            str_header += "0\n};"

        h_file = open(h_file_name, "w")
        h_file.write(str_header)
        h_file.close()
        print("-- " + h_file_name + " is created")
    else:
        print("-- " + h_file_name + " is up to date. skipping.")

if __name__ == '__main__':
    sys.exit(convert())
else:
    print("try without python")
<commit_msg>back to the __future__<commit_after>#!/usr/bin/env python
#-------------------------------------------------------------------------------------------------------
# Copyright (C) Microsoft. All rights reserved.
# Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
#-------------------------------------------------------------------------------------------------------

from __future__ import print_function, with_statement # py 2.5
import sys
import os

def print_usage():
    print("jstoc tool")
    print("creates a (const char array) from a javascript file.")
    print("")
    print("usage: jstoc.py <js file path> <variable name>")
    sys.exit(1)

def convert():
    if len(sys.argv) < 3:
        print_usage()

    js_file_name = sys.argv[1]
    if os.path.isfile(js_file_name) == False:
        print_usage()

    h_file_name = js_file_name + '.h'

    js_file_time = os.path.getmtime(js_file_name)
    h_file_time = 0
    if os.path.isfile(h_file_name):
        h_file_time = os.path.getmtime(h_file_name)

    str_header = "static const char " + sys.argv[2] + "[] = {\n"

    # if header file is up to date and no update to jstoc.py since, skip..
    if h_file_time < js_file_time or h_file_time < os.path.getmtime(sys.argv[0]):
        with open(js_file_name, "rb") as js_file:
            last_break = len(str_header) # skip first line
            byte = js_file.read(1)
            while byte:
                str_header += str(ord(byte))
                str_header += ","
                # beautify a bit. limit column to ~80
                column_check = (len(str_header) + 1) - last_break
                if column_check > 5 and column_check % 80 < 5:
                    last_break = len(str_header) + 1
                    str_header += "\n"
                else:
                    str_header += " "
                byte = js_file.read(1)
            str_header += "0\n};"

        h_file = open(h_file_name, "w")
        h_file.write(str_header)
        h_file.close()
        print("-- " + h_file_name + " is created")
    else:
        print("-- " + h_file_name + " is up to date. skipping.")

if __name__ == '__main__':
    sys.exit(convert())
else:
    print("try without python")
<|endoftext|>


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

from django.db import models, transaction
from django.core.exceptions import ValidationError

from base.models import ScoutChief, EventHappening

class ScoutChiefSubscription(models.Model):

    scout_chief = models.ForeignKey(ScoutChief)
    event_happening = models.ForeignKey(EventHappening)
    is_locked = models.BooleanField(default=False, verbose_name=u"non si può cancellare")

    subscribed_on = models.DateTimeField(auto_now_add=True)
    unsubscribed_on = models.DateTimeField(null=True, blank=True)

    MAX_SUBSCRIPTIONS = 3

    class Meta:

        db_table = "subscriptions"
        verbose_name = "iscrizione evento"
        verbose_name_plural = "iscrizioni eventi"

    def __unicode__(self):
        return u"%s - %s" % (self.scout_chief, self.event_happening)

    def clean(self):
        """
        Checks among fields go here

        raises ValidationError if not valid
        """

        subscriptions = ScoutChiefSubscription.objects.filter(
            scout_chief=self.scout_chief
        )
        n_subscriptions = subscriptions.count()

        if n_subscriptions >= self.MAX_SUBSCRIPTIONS:
            raise ValidationError(u'Non è possibile iscriversi a più di 3 eventi')

        #check no eventi sovrapposti
        if subscriptions.filter(event_happening__timeslot=self.event_happening.timeslot).count():
            raise ValidationError(u'Capo già iscritto ad un evento di questo turno')

        #check che ci siano posti liberi
        if not self.event_happening.available_seats:
            raise ValidationError(
                u"Posti esauriti per l'evento %s, si prega di ricaricare la pagina" % self.event_happening
            )

    def save(self, *args, **kw):
        self.full_clean()
        eh = self.event_happening
        rv = super(ScoutChiefSubscription, self).save(*args, **kw)
        eh.seats_n_chiefs += 1
        eh.save()
        return rv

    @transaction.atomic
    def delete(self):
        if self.is_locked:
            raise ValidationError(u"%s è vincolato a %s: non è possibile eliminarlo" % (
                self.scout_chief, self.event_happening
            ))

        eh = self.event_happening
        rv = super(ScoutChiefSubscription, self).delete()
        eh.seats_n_chiefs -= 1
        eh.save()
        return rv

#---------------------------------------------------------------------------------
# RABBITMQ part

from base.models import Event

MODEL_RABBITMQ_MAP = {
       EventHappening : 'event_happening',
       Event : 'event'
}

def get_rabbitmq_routing_key(sender, instance, created):

    basename = MODEL_RABBITMQ_MAP.get(sender)
    if basename:
        action = ['update','insert'][int(created)]
        return u"%s.%s" % (basename, action)
    else:
        return None

import pika
from django.core import serializers
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
@receiver(post_save)
def my_log_queue(sender, instance, created, **kwargs):

    data = serializers.serialize("json", [instance])

    # Publish changes to RabbitMQ server
    routing_key = get_rabbitmq_routing_key(sender, instance, created)

    if routing_key:
        #RABBITMQ_SETTINGS

        RABBITMQ_connection = pika.BlockingConnection(
            pika.ConnectionParameters(**settings.RABBITMQ)
        )
        RABBITMQ_channel = RABBITMQ_connection.channel()

        RABBITMQ_channel.basic_publish(
            exchange='application', routing_key=routing_key, body=data
        )
        RABBITMQ_connection.close()

<commit_msg>fix bug for rabbitMQ: wrong routing key<commit_after>#-*- coding: utf-8 -*-

from django.db import models, transaction
from django.core.exceptions import ValidationError

from base.models import ScoutChief, EventHappening

class ScoutChiefSubscription(models.Model):

    scout_chief = models.ForeignKey(ScoutChief)
    event_happening = models.ForeignKey(EventHappening)
    is_locked = models.BooleanField(default=False, verbose_name=u"non si può cancellare")

    subscribed_on = models.DateTimeField(auto_now_add=True)
    unsubscribed_on = models.DateTimeField(null=True, blank=True)

    MAX_SUBSCRIPTIONS = 3

    class Meta:

        db_table = "subscriptions"
        verbose_name = "iscrizione evento"
        verbose_name_plural = "iscrizioni eventi"

    def __unicode__(self):
        return u"%s - %s" % (self.scout_chief, self.event_happening)

    def clean(self):
        """
        Checks among fields go here

        raises ValidationError if not valid
        """

        subscriptions = ScoutChiefSubscription.objects.filter(
            scout_chief=self.scout_chief
        )
        n_subscriptions = subscriptions.count()

        if n_subscriptions >= self.MAX_SUBSCRIPTIONS:
            raise ValidationError(u'Non è possibile iscriversi a più di 3 eventi')

        #check no eventi sovrapposti
        if subscriptions.filter(event_happening__timeslot=self.event_happening.timeslot).count():
            raise ValidationError(u'Capo già iscritto ad un evento di questo turno')

        #check che ci siano posti liberi
        if not self.event_happening.available_seats:
            raise ValidationError(
                u"Posti esauriti per l'evento %s, si prega di ricaricare la pagina" % self.event_happening
            )

    def save(self, *args, **kw):
        self.full_clean()
        eh = self.event_happening
        rv = super(ScoutChiefSubscription, self).save(*args, **kw)
        eh.seats_n_chiefs += 1
        eh.save()
        return rv

    @transaction.atomic
    def delete(self):
        if self.is_locked:
            raise ValidationError(u"%s è vincolato a %s: non è possibile eliminarlo" % (
                self.scout_chief, self.event_happening
            ))

        eh = self.event_happening
        rv = super(ScoutChiefSubscription, self).delete()
        eh.seats_n_chiefs -= 1
        eh.save()
        return rv

#---------------------------------------------------------------------------------
# RABBITMQ part

from base.models import Event

MODEL_RABBITMQ_MAP = {
       EventHappening : 'event_happening',
       Event : 'campevents'
}

def get_rabbitmq_routing_key(sender, instance, created):

    basename = MODEL_RABBITMQ_MAP.get(sender)
    if basename:
        action = ['update','insert'][int(created)]
        return u"%s.%s" % (basename, action)
    else:
        return None

import pika
from django.core import serializers
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
@receiver(post_save)
def my_log_queue(sender, instance, created, **kwargs):

    data = serializers.serialize("json", [instance])

    # Publish changes to RabbitMQ server
    routing_key = get_rabbitmq_routing_key(sender, instance, created)

    if routing_key:
        #RABBITMQ_SETTINGS

        RABBITMQ_connection = pika.BlockingConnection(
            pika.ConnectionParameters(**settings.RABBITMQ)
        )
        RABBITMQ_channel = RABBITMQ_connection.channel()

        RABBITMQ_channel.basic_publish(
            exchange='application', routing_key=routing_key, body=data
        )
        RABBITMQ_connection.close()

<|endoftext|>


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

from django.db import models
from django.core.exceptions import ValidationError

from base.models import ScoutChief, EventHappening

class ScoutChiefSubscription(models.Model):

    scout_chief = models.ForeignKey(ScoutChief)
    event_happening = models.ForeignKey(EventHappening)
    is_locked = models.BooleanField(default=False, verbose_name=u"non si può cancellare")

    subscribed_on = models.DateTimeField(auto_now_add=True)
    unsubscribed_on = models.DateTimeField(null=True, blank=True)

    MAX_SUBSCRIPTIONS = 3

    class Meta:

        db_table = "subscriptions"
        verbose_name = "iscrizione evento"
        verbose_name_plural = "iscrizioni eventi"

    def __unicode__(self):
        return u"%s - %s" % (self.scout_chief, self.event_happening)

    def clean(self):
        """
        Checks among fields go here

        raises ValidationError if not valid
        """

        subscriptions = ScoutChiefSubscription.objects.filter(
            scout_chief=self.scout_chief
        )
        n_subscriptions = subscriptions.count()

        if n_subscriptions >= self.MAX_SUBSCRIPTIONS:
            raise ValidationError(u'Non è possibile iscriversi a più di 3 eventi')

        #check no eventi sovrapposti
        if subscriptions.filter(event_happening__timeslot=self.event_happening.timeslot).count():
            raise ValidationError(u'Sei già iscritto ad un evento di questo turno')

        #TODO: check che ci siano posti liberi

    def save(self, *args, **kw):
        self.full_clean()
        return super(ScoutChiefSubscription, self).save(*args, **kw)

    def delete(self):
        if self.is_locked:
            raise ValidationError(u"%s è vincolato a %s: non è possibile eliminarlo" % (
                self.scout_chief, self.event_happening
            ))
        return super(ScoutChiefSubscription, self).delete()
<commit_msg>Aggiornato coerentemente il campo `seats_n_chiefs` di EventHappening ad ogni nuova iscrizione o cancellazione (ScoutChiefSubscription)<commit_after>#-*- coding: utf-8 -*-

from django.db import models, transaction
from django.core.exceptions import ValidationError

from base.models import ScoutChief, EventHappening

class ScoutChiefSubscription(models.Model):

    scout_chief = models.ForeignKey(ScoutChief)
    event_happening = models.ForeignKey(EventHappening)
    is_locked = models.BooleanField(default=False, verbose_name=u"non si può cancellare")

    subscribed_on = models.DateTimeField(auto_now_add=True)
    unsubscribed_on = models.DateTimeField(null=True, blank=True)

    MAX_SUBSCRIPTIONS = 3

    class Meta:

        db_table = "subscriptions"
        verbose_name = "iscrizione evento"
        verbose_name_plural = "iscrizioni eventi"

    def __unicode__(self):
        return u"%s - %s" % (self.scout_chief, self.event_happening)

    def clean(self):
        """
        Checks among fields go here

        raises ValidationError if not valid
        """

        subscriptions = ScoutChiefSubscription.objects.filter(
            scout_chief=self.scout_chief
        )
        n_subscriptions = subscriptions.count()

        if n_subscriptions >= self.MAX_SUBSCRIPTIONS:
            raise ValidationError(u'Non è possibile iscriversi a più di 3 eventi')

        #check no eventi sovrapposti
        if subscriptions.filter(event_happening__timeslot=self.event_happening.timeslot).count():
            raise ValidationError(u'Sei già iscritto ad un evento di questo turno')

        #TODO: check che ci siano posti liberi

    def save(self, *args, **kw):
        self.full_clean()
        eh = self.event_happening
        rv = super(ScoutChiefSubscription, self).save(*args, **kw)
        eh.seats_n_chiefs += 1
        eh.save()
        return rv

    @transaction.commit_on_success
    def delete(self):
        if self.is_locked:
            raise ValidationError(u"%s è vincolato a %s: non è possibile eliminarlo" % (
                self.scout_chief, self.event_happening
            ))

        eh = self.event_happening
        rv = super(ScoutChiefSubscription, self).delete()
        eh.seats_n_chiefs -= 1
        eh.save()
        return rv
<|endoftext|>


<commit_before>from arches.setup import get_version

VERSION = (4, 1, 1, 'final', 0)

__version__ = get_version(VERSION)<commit_msg>Updates version number to 4.3<commit_after>from arches.setup import get_version

VERSION = (4, 3, 0, 'final', 0)

__version__ = get_version(VERSION)
<|endoftext|>


<commit_before>import re
import sys
import ast
import inspect
import difflib
import traceback
import collections

import archon.common

class command(archon.common.denoter):
    """
    Denote a function as an command.
    """
    commandData = collections.defaultdict(None)

    def __call__(self, func):
        def closure(output, context, player, *args):
            argspec = inspect.getfullargspec(func)
            if argspec.varargs in argspec.annotations:
                args = argspec.annotations[argspec.varargs](
                    output, context, player, *args
                    )
                if not isinstance(args, collections.Iterable):
                    args = [args]
            res = func(output, context, player, *args)
            if not res:
                res = context
            return res

        closure.__name__ = func.__name__
        return super().__call__(closure)

    @property
    def data(self):
        return self.__class__.commandData[self.names[0]]

    @classmethod
    def nearest(cls, name):
        return difflib.get_close_matches(name, list(cls.functions.keys()))

def find(output, context, player, *args):
    if ' '.join(args) in ('me', 'myself'):
        return player
    matches = context.naturalFind(' '.join(args))
    if not matches:
        raise output.error('No entity found.')
    elif isinstance(matches, set):
        raise output.error('Please be more specific.')
    else:
        entity = context.allContents[matches]
        return entity, context.entityCache.lookup(entity[0])
    return None

def findInventory(output, context, player, *args):
    '''Lookup an item by friendly name or index.'''
    if not args:
        return []
    criterion = ' '.join(args)
    try:
        criterion = int(criterion)
        return list(sorted(player.attributes.inventory))[criterion]
    except IndexError:
        raise output.error("Invalid index.")
    except ValueError:
        for item in player.attributes.inventory:
            if item.friendlyName == criterion:
                return item
        raise output.error("Item not found.")

@command('inventory')
def inventory(output, context, player, *args):
    output.display(
        'Inventory ({length})'.format(
            length=len(player.attributes.inventory)
            ))
    for item in sorted(player.attributes.inventory,
                       key=lambda k:k.friendlyName):
        output.display(item.friendlyName)
    return context

@command('equip')
def equip(output, context, player, *args:findInventory):
    if not args:
        for slot, item in player.attributes.equip.items():
            output.display('{slot}: {item}'.format(
                    slot=slot, item=item.friendlyName
                    ))
    else:
        args = args[0]
        if not args.attributes.get('equip'):
            raise output.error('Cannot equip item.')
        elif (list(player.attributes.equip.values()).count(args) ==
              player.attributes.inventory.count(args)):
            raise output.error('Already equipped all of that item.')
        else:
            possibleSlots = args.attributes['equip']
            equip = player.attributes.equip
            for slot in possibleSlots:
                if slot not in equip or not equip[slot]:
                    equip[slot] = args
                    break
            equip[possibleSlots[0]] = args

@command('test.restart')
def test(output, context, player, *args):
    '''Restart the Archon session, if possible. This is a test command.'''
    output.restart()
    return context

@command('take')
def take(output, context, player, *item: find):
    if not item or not item[1].attributes.get('take', False):
        output.error("You can't take that.")
    else:
        data, item = item
        player.attributes.inventory.append(item)
        del context.contents[data.key]

useFunctionRe = re.compile(
    r'(?P<function>[a-zA-Z0-9]+)\((?P<arguments>[\S]+)\)'
    )

@command('use')
def use(output, context, player, *item: find):
    '''Use an object.'''
    data, item = item
    if not item or not 'use' in item.attributes:
        output.error("You can't use that.")
        return
    usage = item.attributes['use']
    result = useFunctionRe.match(usage)
    if result:
        result = result.groupdict()
        function, arguments = result['function'], result['arguments']
        arguments = [ast.literal_eval(x.strip())
                     for x in arguments.split(',')]
        # this is hardcoded since it's the only one supported now
        # currently, it simply runs the script with some globals
        # in the future, an entity should specify what functions in the
        # script should be run, e.g. function(script, functionName)
        # idea: use function annotations to determine which argument
        # represents which desired object
        if function == 'script':
            script = context.entityCache[arguments[0]]
            namespace = {'output': output,
                         'context': context,
                         'player': player}
            try:
                exec(script, namespace)
                if 'elapsedTime' in namespace:
                    context.attributes['time'] += namespace['elapsedTime']
            except:  # yes, everything
                output.error("It doesn't work.")
                if output.permissions.get('debug', False):
                    output.error(traceback.format_exc())
        else:
            output.error("It doesn't work.")
    else:
        output.error("You can't use that.")

@command('go')
def go(output, context, player, *args):
    '''Go in the specified direction.'''
    direction = args[0]  # XXX multiword directions?
    target = context.outputs.get(direction)
    if target:
        target.enter(context.exit())
        return command.get('describe')(output, target, player)
    else:
        output.error("You can't go that way.")
        return context

@command('enter')
def enter(output, context, player, *args):
    '''
    If the specified entity is a teleport (e.g. a door), use it.

    Also, create an entry in the history chain of visited rooms.
    '''
    teleport = context.naturalFind(' '.join(args))
    if not teleport:
        output.error('No entity found.')
    elif isinstance(teleport, set):
        output.error('Please be more specific.')
    else:
        entityData = context.allContents[teleport]
        for option in entityData.options:
            if option.startswith('to:'):
                target = option[3:].strip()
                target = context.entityCache[target]
                if output.question(
                    "Go to {}?".format(target.attributes['friendlyName'])
                    ):
                    target.enter(context.exit())
                    return command.get('describe')(output, target, player)
    return context  # we failed teleporting

@command('exit', 'back')
def exit(output, context, player, *args):
    '''
    Return to the previous room, unless the history chain was reset.
    '''

@command('describe')
def describe(output, context, player, *args):
    '''Describe the current room or the specified object.'''
    if args:
        if args[0].lower() in ('me', 'myself'):
            output.display(player.describe())
            return context
        matches = context.naturalFind(' '.join(args))
        if not matches:
            output.display("What are you talking about?")
        elif isinstance(matches, set):
            output.error("That was ambiguous.")
            output.display("Did you mean:")
            for match in matches:
                edata = context.contents[match]
                output.display("\t{prefix}{identity}".format(
                    prefix=edata[4] + ' ',
                    identity=edata[1]
                    ))
        else:
            output.display(context.describe(matches, verbose=True))
    else:
        output.display(context.describe())

@command('quit', 'test.exit')
def quit(output, context, player, *args):
    output.quit()

@command('help')
def help(output, context, player, *args):
    '''Provides help to the player.'''
    if args:
        try:
            output.display(
                trimDocstring(command.get(args[0]).__doc__)
                )
        except CommandNotFoundError:
            output.error('Command {} not found!'.format(args[0]))
            close = command.nearest(args[0])
            if close:
                output.display('Did you mean: {}'.format(close[0]))
    else:
        output.display(STRING_HELP)

def trimDocstring(docstring):
    '''
    Trim the whitespace in a docstring as per PEP257.

    The contents of PEP257, from which this function was taken, are in the
    public domain.
    '''
    if not docstring:
        return ''
    # Convert tabs to spaces (following the normal Python rules)
    # and split into a list of lines:
    lines = docstring.expandtabs().splitlines()
    # Determine minimum indentation (first line doesn't count):
    indent = sys.maxsize
    for line in lines[1:]:
        stripped = line.lstrip()
        if stripped:
            indent = min(indent, len(line) - len(stripped))
    # Remove indentation (first line is special):
    trimmed = [lines[0].strip()]
    if indent < sys.maxsize:
        for line in lines[1:]:
            trimmed.append(line[indent:].rstrip())
    # Strip off trailing and leading blank lines:
    while trimmed and not trimmed[-1]:
        trimmed.pop()
    while trimmed and not trimmed[0]:
        trimmed.pop(0)
    # Return a single string:
    return '\n'.join(trimmed)

STRING_HELP = '''
Welcome to the Archon demo. Type 'help [name]' for help on a specific
command or topic. If you're just starting, try 'describe' to see what your
area is like.
'''
<commit_msg>Added unequip command<commit_after>import re
import sys
import ast
import inspect
import difflib
import traceback
import collections

import archon.common

class command(archon.common.denoter):
    """
    Denote a function as an command.
    """
    commandData = collections.defaultdict(None)

    def __call__(self, func):
        def closure(output, context, player, *args):
            argspec = inspect.getfullargspec(func)
            if argspec.varargs in argspec.annotations:
                args = argspec.annotations[argspec.varargs](
                    output, context, player, *args
                    )
                if not isinstance(args, collections.Iterable):
                    args = [args]
            res = func(output, context, player, *args)
            if not res:
                res = context
            return res

        closure.__name__ = func.__name__
        return super().__call__(closure)

    @property
    def data(self):
        return self.__class__.commandData[self.names[0]]

    @classmethod
    def nearest(cls, name):
        return difflib.get_close_matches(name, list(cls.functions.keys()))

def find(output, context, player, *args):
    if ' '.join(args) in ('me', 'myself'):
        return player
    matches = context.naturalFind(' '.join(args))
    if not matches:
        raise output.error('No entity found.')
    elif isinstance(matches, set):
        raise output.error('Please be more specific.')
    else:
        entity = context.allContents[matches]
        return entity, context.entityCache.lookup(entity[0])
    return None

def findInventory(output, context, player, *args):
    '''Lookup an item by friendly name or index.'''
    if not args:
        return []
    criterion = ' '.join(args)
    try:
        criterion = int(criterion)
        return list(sorted(player.attributes.inventory))[criterion]
    except IndexError:
        raise output.error("Invalid index.")
    except ValueError:
        for item in player.attributes.inventory:
            if item.friendlyName == criterion:
                return item
        raise output.error("Item not found.")

def findEquip(output, context, player, *args):
    if not args:
        return []
    criterion = ' '.join(args)
    for slot, item in player.attributes.equip.items():
        if criterion in (slot, item.friendlyName):
            player.attributes.equip[slot] = None
            output.display('Unequipped item {} from {}'.format(
                    item.friendlyName, slot))

@command('inventory')
def inventory(output, context, player, *args):
    output.display(
        'Inventory ({length})'.format(
            length=len(player.attributes.inventory)
            ))
    for item in sorted(player.attributes.inventory,
                       key=lambda k: k.friendlyName):
        output.display(item.friendlyName)
    return context

@command('equip')
def equip(output, context, player, *args: findInventory):
    if not args:
        for slot, item in player.attributes.equip.items():
            if item is None:
                item = '<Empty>'
            else:
                item=item.friendlyName
            output.display('{slot}: {item}'.format(
                    slot=slot, item=item
                    ))
    else:
        args = args[0]
        if not args.attributes.get('equip'):
            raise output.error('Cannot equip item.')
        elif (list(player.attributes.equip.values()).count(args) ==
              player.attributes.inventory.count(args)):
            raise output.error('Already equipped all of that item.')
        else:
            possibleSlots = args.attributes['equip']
            equip = player.attributes.equip
            for slot in possibleSlots:
                if slot not in equip or not equip[slot]:
                    equip[slot] = args
                    break
            equip[possibleSlots[0]] = args

@command('unequip')
def unequip(output, context, player, *args: findEquip):
    args = args[0]

@command('test.restart')
def test(output, context, player, *args):
    '''Restart the Archon session, if possible. This is a test command.'''
    output.restart()
    return context

@command('take')
def take(output, context, player, *item: find):
    if not item or not item[1].attributes.get('take', False):
        output.error("You can't take that.")
    else:
        data, item = item
        player.attributes.inventory.append(item)
        del context.contents[data.key]

useFunctionRe = re.compile(
    r'(?P<function>[a-zA-Z0-9]+)\((?P<arguments>[\S]+)\)'
    )

@command('use')
def use(output, context, player, *item: find):
    '''Use an object.'''
    data, item = item
    if not item or not 'use' in item.attributes:
        output.error("You can't use that.")
        return
    usage = item.attributes['use']
    result = useFunctionRe.match(usage)
    if result:
        result = result.groupdict()
        function, arguments = result['function'], result['arguments']
        arguments = [ast.literal_eval(x.strip())
                     for x in arguments.split(',')]
        # this is hardcoded since it's the only one supported now
        # currently, it simply runs the script with some globals
        # in the future, an entity should specify what functions in the
        # script should be run, e.g. function(script, functionName)
        # idea: use function annotations to determine which argument
        # represents which desired object
        if function == 'script':
            script = context.entityCache[arguments[0]]
            namespace = {'output': output,
                         'context': context,
                         'player': player}
            try:
                exec(script, namespace)
                if 'elapsedTime' in namespace:
                    context.attributes['time'] += namespace['elapsedTime']
            except:  # yes, everything
                output.error("It doesn't work.")
                if output.permissions.get('debug', False):
                    output.error(traceback.format_exc())
        else:
            output.error("It doesn't work.")
    else:
        output.error("You can't use that.")

@command('go')
def go(output, context, player, *args):
    '''Go in the specified direction.'''
    direction = args[0]  # XXX multiword directions?
    target = context.outputs.get(direction)
    if target:
        target.enter(context.exit())
        return command.get('describe')(output, target, player)
    else:
        output.error("You can't go that way.")
        return context

@command('enter')
def enter(output, context, player, *args):
    '''
    If the specified entity is a teleport (e.g. a door), use it.

    Also, create an entry in the history chain of visited rooms.
    '''
    teleport = context.naturalFind(' '.join(args))
    if not teleport:
        output.error('No entity found.')
    elif isinstance(teleport, set):
        output.error('Please be more specific.')
    else:
        entityData = context.allContents[teleport]
        for option in entityData.options:
            if option.startswith('to:'):
                target = option[3:].strip()
                target = context.entityCache[target]
                if output.question(
                    "Go to {}?".format(target.attributes['friendlyName'])
                    ):
                    target.enter(context.exit())
                    return command.get('describe')(output, target, player)
    return context  # we failed teleporting

@command('exit', 'back')
def exit(output, context, player, *args):
    '''
    Return to the previous room, unless the history chain was reset.
    '''

@command('describe')
def describe(output, context, player, *args):
    '''Describe the current room or the specified object.'''
    if args:
        if args[0].lower() in ('me', 'myself'):
            output.display(player.describe())
            return context
        matches = context.naturalFind(' '.join(args))
        if not matches:
            output.display("What are you talking about?")
        elif isinstance(matches, set):
            output.error("That was ambiguous.")
            output.display("Did you mean:")
            for match in matches:
                edata = context.contents[match]
                output.display("\t{prefix}{identity}".format(
                    prefix=edata[4] + ' ',
                    identity=edata[1]
                    ))
        else:
            output.display(context.describe(matches, verbose=True))
    else:
        output.display(context.describe())

@command('quit', 'test.exit')
def quit(output, context, player, *args):
    output.quit()

@command('help')
def help(output, context, player, *args):
    '''Provides help to the player.'''
    if args:
        try:
            output.display(
                trimDocstring(command.get(args[0]).__doc__)
                )
        except CommandNotFoundError:
            output.error('Command {} not found!'.format(args[0]))
            close = command.nearest(args[0])
            if close:
                output.display('Did you mean: {}'.format(close[0]))
    else:
        output.display(STRING_HELP)

def trimDocstring(docstring):
    '''
    Trim the whitespace in a docstring as per PEP257.

    The contents of PEP257, from which this function was taken, are in the
    public domain.
    '''
    if not docstring:
        return ''
    # Convert tabs to spaces (following the normal Python rules)
    # and split into a list of lines:
    lines = docstring.expandtabs().splitlines()
    # Determine minimum indentation (first line doesn't count):
    indent = sys.maxsize
    for line in lines[1:]:
        stripped = line.lstrip()
        if stripped:
            indent = min(indent, len(line) - len(stripped))
    # Remove indentation (first line is special):
    trimmed = [lines[0].strip()]
    if indent < sys.maxsize:
        for line in lines[1:]:
            trimmed.append(line[indent:].rstrip())
    # Strip off trailing and leading blank lines:
    while trimmed and not trimmed[-1]:
        trimmed.pop()
    while trimmed and not trimmed[0]:
        trimmed.pop(0)
    # Return a single string:
    return '\n'.join(trimmed)

STRING_HELP = '''
Welcome to the Archon demo. Type 'help [name]' for help on a specific
command or topic. If you're just starting, try 'describe' to see what your
area is like.
'''
<|endoftext|>


<commit_before>import os
import random
import weakref

from aredis.client import StrictRedis
from aredis.connection import Connection
from aredis.pool import ConnectionPool
from aredis.exceptions import (ConnectionError, ResponseError, ReadOnlyError,
                              TimeoutError)
from aredis.utils import iteritems

class MasterNotFoundError(ConnectionError):
    pass

class SlaveNotFoundError(ConnectionError):
    pass

class SentinelManagedConnection(Connection):
    def __init__(self, **kwargs):
        self.connection_pool = kwargs.pop('connection_pool')
        super(SentinelManagedConnection, self).__init__(**kwargs)

    def __repr__(self):
        pool = self.connection_pool
        if self.host:
            host_info = ',host=%s,port=%s' % (self.host, self.port)
        else:
            host_info = ''
        s = '{}<service={}{}>'.format(type(self).__name__, pool.service_name, host_info)
        return s

    async def connect_to(self, address):
        self.host, self.port = address
        await super(SentinelManagedConnection, self).connect()
        if self.connection_pool.check_connection:
            await self.send_command('PING')
            if await self.read_response() != b'PONG':
                raise ConnectionError('PING failed')

    async def connect(self):
        if self._reader and self._writer:
            return  # already connected
        if self.connection_pool.is_master:
            await self.connect_to(await self.connection_pool.get_master_address())
        else:
            for slave in await self.connection_pool.rotate_slaves():
                try:
                    return await self.connect_to(slave)
                except ConnectionError:
                    continue
            raise SlaveNotFoundError  # Never be here

    async def read_response(self):
        try:
            return await super(SentinelManagedConnection, self).read_response()
        except ReadOnlyError:
            if self.connection_pool.is_master:
                # When talking to a master, a ReadOnlyError when likely
                # indicates that the previous master that we're still connected
                # to has been demoted to a slave and there's a new master.
                # calling disconnect will force the connection to re-query
                # sentinel during the next connect() attempt.
                self.disconnect()
                raise ConnectionError('The previous master is now a slave')
            raise

class SentinelConnectionPool(ConnectionPool):
    """
    Sentinel backed connection pool.

    If ``check_connection`` flag is set to True, SentinelManagedConnection
    sends a PING command right after establishing the connection.
    """

    def __init__(self, service_name, sentinel_manager, **kwargs):
        kwargs['connection_class'] = kwargs.get(
            'connection_class', SentinelManagedConnection)
        self.is_master = kwargs.pop('is_master', True)
        self.check_connection = kwargs.pop('check_connection', False)
        super(SentinelConnectionPool, self).__init__(**kwargs)
        self.connection_kwargs['connection_pool'] = weakref.proxy(self)
        self.service_name = service_name
        self.sentinel_manager = sentinel_manager

    def __repr__(self):
        return "{}<service={}({})".format(
            type(self).__name__,
            self.service_name,
            self.is_master and 'master' or 'slave',
        )

    def reset(self):
        super(SentinelConnectionPool, self).reset()
        self.master_address = None
        self.slave_rr_counter = None

    async def get_master_address(self):
        master_address = await self.sentinel_manager.discover_master(
            self.service_name)
        if self.is_master:
            if self.master_address is None:
                self.master_address = master_address
            elif master_address != self.master_address:
                # Master address changed, disconnect all clients in this pool
                self.disconnect()
        return master_address

    async def rotate_slaves(self):
        "Round-robin slave balancer"
        slaves = await self.sentinel_manager.discover_slaves(self.service_name)
        slave_address = list()
        if slaves:
            if self.slave_rr_counter is None:
                self.slave_rr_counter = random.randint(0, len(slaves) - 1)
            for _ in range(len(slaves)):
                self.slave_rr_counter = (
                    self.slave_rr_counter + 1) % len(slaves)
                slave_address.append(slaves[self.slave_rr_counter])
                # todo change into generator in Python 3.6
                # yield slave
            return slave_address
        # Fallback to the master connection
        try:
            return await self.get_master_address()
        except MasterNotFoundError:
            pass
        raise SlaveNotFoundError('No slave found for %r' % (self.service_name))

    def _checkpid(self):
        if self.pid != os.getpid():
            self.disconnect()
            self.reset()
            self.__init__(self.service_name, self.sentinel_manager,
                          is_master=self.is_master,
                          check_connection=self.check_connection,
                          connection_class=self.connection_class,
                          max_connections=self.max_connections,
                          **self.connection_kwargs)

class Sentinel(object):
    """
    Redis Sentinel cluster client

    >>> from aredis.sentinel import Sentinel
    >>> sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1)
    >>> async def test():
    >>>     master = await sentinel.master_for('mymaster', socket_timeout=0.1)
    >>>     await master.set('foo', 'bar')
    >>>     slave = await sentinel.slave_for('mymaster', socket_timeout=0.1)
    >>>     await slave.get('foo')

    ``sentinels`` is a list of sentinel nodes. Each node is represented by
    a pair (hostname, port).

    ``min_other_sentinels`` defined a minimum number of peers for a sentinel.
    When querying a sentinel, if it doesn't meet this threshold, responses
    from that sentinel won't be considered valid.

    ``sentinel_kwargs`` is a dictionary of connection arguments used when
    connecting to sentinel instances. Any argument that can be passed to
    a normal Redis connection can be specified here. If ``sentinel_kwargs`` is
    not specified, any socket_timeout and socket_keepalive options specified
    in ``connection_kwargs`` will be used.

    ``connection_kwargs`` are keyword arguments that will be used when
    establishing a connection to a Redis server.
    """

    def __init__(self, sentinels, min_other_sentinels=0, sentinel_kwargs=None,
                 **connection_kwargs):
        # if sentinel_kwargs isn't defined, use the socket_* options from
        # connection_kwargs
        if sentinel_kwargs is None:
            sentinel_kwargs = dict([(k, v)
                                    for k, v in iteritems(connection_kwargs)
                                    if k.startswith('socket_')
                                    ])
        self.sentinel_kwargs = sentinel_kwargs

        self.sentinels = [StrictRedis(hostname, port, **self.sentinel_kwargs)
                          for hostname, port in sentinels]
        self.min_other_sentinels = min_other_sentinels
        self.connection_kwargs = connection_kwargs

    def __repr__(self):
        sentinel_addresses = []
        for sentinel in self.sentinels:
            sentinel_addresses.append('{}:{}'.format(
                sentinel.connection_pool.connection_kwargs['host'],
                sentinel.connection_pool.connection_kwargs['port'],
            ))
        return '{}<sentinels=[{}]>'.format(
            type(self).__name__,
            ','.join(sentinel_addresses))

    def check_master_state(self, state, service_name):
        if not state['is_master'] or state['is_sdown'] or state['is_odown']:
            return False
        # Check if our sentinel doesn't see other nodes
        if state['num-other-sentinels'] < self.min_other_sentinels:
            return False
        return True

    async def discover_master(self, service_name):
        """
        Asks sentinel servers for the Redis master's address corresponding
        to the service labeled ``service_name``.

        Returns a pair (address, port) or raises MasterNotFoundError if no
        master is found.
        """
        for sentinel_no, sentinel in enumerate(self.sentinels):
            try:
                masters = await sentinel.sentinel_masters()
            except (ConnectionError, TimeoutError):
                continue
            state = masters.get(service_name)
            if state and self.check_master_state(state, service_name):
                # Put this sentinel at the top of the list
                self.sentinels[0], self.sentinels[sentinel_no] = (
                    sentinel, self.sentinels[0])
                return state['ip'], state['port']
        raise MasterNotFoundError("No master found for %r" % (service_name,))

    def filter_slaves(self, slaves):
        "Remove slaves that are in an ODOWN or SDOWN state"
        slaves_alive = []
        for slave in slaves:
            if slave['is_odown'] or slave['is_sdown']:
                continue
            slaves_alive.append((slave['ip'], slave['port']))
        return slaves_alive

    async def discover_slaves(self, service_name):
        "Returns a list of alive slaves for service ``service_name``"
        for sentinel in self.sentinels:
            try:
                slaves = await sentinel.sentinel_slaves(service_name)
            except (ConnectionError, ResponseError, TimeoutError):
                continue
            slaves = self.filter_slaves(slaves)
            if slaves:
                return slaves
        return []

    def master_for(self, service_name, redis_class=StrictRedis,
                   connection_pool_class=SentinelConnectionPool, **kwargs):
        """
        Returns a redis client instance for the ``service_name`` master.

        A SentinelConnectionPool class is used to retrive the master's
        address before establishing a new connection.

        NOTE: If the master's address has changed, any cached connections to
        the old master are closed.

        By default clients will be a redis.StrictRedis instance. Specify a
        different class to the ``redis_class`` argument if you desire
        something different.

        The ``connection_pool_class`` specifies the connection pool to use.
        The SentinelConnectionPool will be used by default.

        All other keyword arguments are merged with any connection_kwargs
        passed to this class and passed to the connection pool as keyword
        arguments to be used to initialize Redis connections.
        """
        kwargs['is_master'] = True
        connection_kwargs = dict(self.connection_kwargs)
        connection_kwargs.update(kwargs)
        return redis_class(connection_pool=connection_pool_class(
            service_name, self, **connection_kwargs))

    def slave_for(self, service_name, redis_class=StrictRedis,
                  connection_pool_class=SentinelConnectionPool, **kwargs):
        """
        Returns redis client instance for the ``service_name`` slave(s).

        A SentinelConnectionPool class is used to retrive the slave's
        address before establishing a new connection.

        By default clients will be a redis.StrictRedis instance. Specify a
        different class to the ``redis_class`` argument if you desire
        something different.

        The ``connection_pool_class`` specifies the connection pool to use.
        The SentinelConnectionPool will be used by default.

        All other keyword arguments are merged with any connection_kwargs
        passed to this class and passed to the connection pool as keyword
        arguments to be used to initialize Redis connections.
        """
        kwargs['is_master'] = False
        connection_kwargs = dict(self.connection_kwargs)
        connection_kwargs.update(kwargs)
        return redis_class(connection_pool=connection_pool_class(
            service_name, self, **connection_kwargs))
<commit_msg>fix todo in sentinel.rotate_slaves<commit_after>import os
import random
import weakref

from aredis.client import StrictRedis
from aredis.connection import Connection
from aredis.pool import ConnectionPool
from aredis.exceptions import (ConnectionError, ResponseError, ReadOnlyError,
                              TimeoutError)
from aredis.utils import iteritems

class MasterNotFoundError(ConnectionError):
    pass

class SlaveNotFoundError(ConnectionError):
    pass

class SentinelManagedConnection(Connection):
    def __init__(self, **kwargs):
        self.connection_pool = kwargs.pop('connection_pool')
        super(SentinelManagedConnection, self).__init__(**kwargs)

    def __repr__(self):
        pool = self.connection_pool
        if self.host:
            host_info = ',host=%s,port=%s' % (self.host, self.port)
        else:
            host_info = ''
        s = '{}<service={}{}>'.format(type(self).__name__, pool.service_name, host_info)
        return s

    async def connect_to(self, address):
        self.host, self.port = address
        await super(SentinelManagedConnection, self).connect()
        if self.connection_pool.check_connection:
            await self.send_command('PING')
            if await self.read_response() != b'PONG':
                raise ConnectionError('PING failed')

    async def connect(self):
        if self._reader and self._writer:
            return  # already connected
        if self.connection_pool.is_master:
            await self.connect_to(await self.connection_pool.get_master_address())
        else:
            for slave in await self.connection_pool.rotate_slaves():
                try:
                    return await self.connect_to(slave)
                except ConnectionError:
                    continue
            raise SlaveNotFoundError  # Never be here

    async def read_response(self):
        try:
            return await super(SentinelManagedConnection, self).read_response()
        except ReadOnlyError:
            if self.connection_pool.is_master:
                # When talking to a master, a ReadOnlyError when likely
                # indicates that the previous master that we're still connected
                # to has been demoted to a slave and there's a new master.
                # calling disconnect will force the connection to re-query
                # sentinel during the next connect() attempt.
                self.disconnect()
                raise ConnectionError('The previous master is now a slave')
            raise

class SentinelConnectionPool(ConnectionPool):
    """
    Sentinel backed connection pool.

    If ``check_connection`` flag is set to True, SentinelManagedConnection
    sends a PING command right after establishing the connection.
    """

    def __init__(self, service_name, sentinel_manager, **kwargs):
        kwargs['connection_class'] = kwargs.get(
            'connection_class', SentinelManagedConnection)
        self.is_master = kwargs.pop('is_master', True)
        self.check_connection = kwargs.pop('check_connection', False)
        super(SentinelConnectionPool, self).__init__(**kwargs)
        self.connection_kwargs['connection_pool'] = weakref.proxy(self)
        self.service_name = service_name
        self.sentinel_manager = sentinel_manager

    def __repr__(self):
        return "{}<service={}({})".format(
            type(self).__name__,
            self.service_name,
            self.is_master and 'master' or 'slave',
        )

    def reset(self):
        super(SentinelConnectionPool, self).reset()
        self.master_address = None
        self.slave_rr_counter = None

    async def get_master_address(self):
        master_address = await self.sentinel_manager.discover_master(
            self.service_name)
        if self.is_master:
            if self.master_address is None:
                self.master_address = master_address
            elif master_address != self.master_address:
                # Master address changed, disconnect all clients in this pool
                self.disconnect()
        return master_address

    async def rotate_slaves(self):
        "Round-robin slave balancer"
        slaves = await self.sentinel_manager.discover_slaves(self.service_name)
        slave_address = list()
        if slaves:
            if self.slave_rr_counter is None:
                self.slave_rr_counter = random.randint(0, len(slaves) - 1)
            for _ in range(len(slaves)):
                self.slave_rr_counter = (
                    self.slave_rr_counter + 1) % len(slaves)
                slave_address.append(slaves[self.slave_rr_counter])
            return slave_address
        # Fallback to the master connection
        try:
            return await self.get_master_address()
        except MasterNotFoundError:
            pass
        raise SlaveNotFoundError('No slave found for %r' % (self.service_name))

    def _checkpid(self):
        if self.pid != os.getpid():
            self.disconnect()
            self.reset()
            self.__init__(self.service_name, self.sentinel_manager,
                          is_master=self.is_master,
                          check_connection=self.check_connection,
                          connection_class=self.connection_class,
                          max_connections=self.max_connections,
                          **self.connection_kwargs)

class Sentinel(object):
    """
    Redis Sentinel cluster client

    >>> from aredis.sentinel import Sentinel
    >>> sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1)
    >>> async def test():
    >>>     master = await sentinel.master_for('mymaster', socket_timeout=0.1)
    >>>     await master.set('foo', 'bar')
    >>>     slave = await sentinel.slave_for('mymaster', socket_timeout=0.1)
    >>>     await slave.get('foo')

    ``sentinels`` is a list of sentinel nodes. Each node is represented by
    a pair (hostname, port).

    ``min_other_sentinels`` defined a minimum number of peers for a sentinel.
    When querying a sentinel, if it doesn't meet this threshold, responses
    from that sentinel won't be considered valid.

    ``sentinel_kwargs`` is a dictionary of connection arguments used when
    connecting to sentinel instances. Any argument that can be passed to
    a normal Redis connection can be specified here. If ``sentinel_kwargs`` is
    not specified, any socket_timeout and socket_keepalive options specified
    in ``connection_kwargs`` will be used.

    ``connection_kwargs`` are keyword arguments that will be used when
    establishing a connection to a Redis server.
    """

    def __init__(self, sentinels, min_other_sentinels=0, sentinel_kwargs=None,
                 **connection_kwargs):
        # if sentinel_kwargs isn't defined, use the socket_* options from
        # connection_kwargs
        if sentinel_kwargs is None:
            sentinel_kwargs = dict([(k, v)
                                    for k, v in iteritems(connection_kwargs)
                                    if k.startswith('socket_')
                                    ])
        self.sentinel_kwargs = sentinel_kwargs

        self.sentinels = [StrictRedis(hostname, port, **self.sentinel_kwargs)
                          for hostname, port in sentinels]
        self.min_other_sentinels = min_other_sentinels
        self.connection_kwargs = connection_kwargs

    def __repr__(self):
        sentinel_addresses = []
        for sentinel in self.sentinels:
            sentinel_addresses.append('{}:{}'.format(
                sentinel.connection_pool.connection_kwargs['host'],
                sentinel.connection_pool.connection_kwargs['port'],
            ))
        return '{}<sentinels=[{}]>'.format(
            type(self).__name__,
            ','.join(sentinel_addresses))

    def check_master_state(self, state, service_name):
        if not state['is_master'] or state['is_sdown'] or state['is_odown']:
            return False
        # Check if our sentinel doesn't see other nodes
        if state['num-other-sentinels'] < self.min_other_sentinels:
            return False
        return True

    async def discover_master(self, service_name):
        """
        Asks sentinel servers for the Redis master's address corresponding
        to the service labeled ``service_name``.

        Returns a pair (address, port) or raises MasterNotFoundError if no
        master is found.
        """
        for sentinel_no, sentinel in enumerate(self.sentinels):
            try:
                masters = await sentinel.sentinel_masters()
            except (ConnectionError, TimeoutError):
                continue
            state = masters.get(service_name)
            if state and self.check_master_state(state, service_name):
                # Put this sentinel at the top of the list
                self.sentinels[0], self.sentinels[sentinel_no] = (
                    sentinel, self.sentinels[0])
                return state['ip'], state['port']
        raise MasterNotFoundError("No master found for %r" % (service_name,))

    def filter_slaves(self, slaves):
        "Remove slaves that are in an ODOWN or SDOWN state"
        slaves_alive = []
        for slave in slaves:
            if slave['is_odown'] or slave['is_sdown']:
                continue
            slaves_alive.append((slave['ip'], slave['port']))
        return slaves_alive

    async def discover_slaves(self, service_name):
        "Returns a list of alive slaves for service ``service_name``"
        for sentinel in self.sentinels:
            try:
                slaves = await sentinel.sentinel_slaves(service_name)
            except (ConnectionError, ResponseError, TimeoutError):
                continue
            slaves = self.filter_slaves(slaves)
            if slaves:
                return slaves
        return []

    def master_for(self, service_name, redis_class=StrictRedis,
                   connection_pool_class=SentinelConnectionPool, **kwargs):
        """
        Returns a redis client instance for the ``service_name`` master.

        A SentinelConnectionPool class is used to retrive the master's
        address before establishing a new connection.

        NOTE: If the master's address has changed, any cached connections to
        the old master are closed.

        By default clients will be a redis.StrictRedis instance. Specify a
        different class to the ``redis_class`` argument if you desire
        something different.

        The ``connection_pool_class`` specifies the connection pool to use.
        The SentinelConnectionPool will be used by default.

        All other keyword arguments are merged with any connection_kwargs
        passed to this class and passed to the connection pool as keyword
        arguments to be used to initialize Redis connections.
        """
        kwargs['is_master'] = True
        connection_kwargs = dict(self.connection_kwargs)
        connection_kwargs.update(kwargs)
        return redis_class(connection_pool=connection_pool_class(
            service_name, self, **connection_kwargs))

    def slave_for(self, service_name, redis_class=StrictRedis,
                  connection_pool_class=SentinelConnectionPool, **kwargs):
        """
        Returns redis client instance for the ``service_name`` slave(s).

        A SentinelConnectionPool class is used to retrive the slave's
        address before establishing a new connection.

        By default clients will be a redis.StrictRedis instance. Specify a
        different class to the ``redis_class`` argument if you desire
        something different.

        The ``connection_pool_class`` specifies the connection pool to use.
        The SentinelConnectionPool will be used by default.

        All other keyword arguments are merged with any connection_kwargs
        passed to this class and passed to the connection pool as keyword
        arguments to be used to initialize Redis connections.
        """
        kwargs['is_master'] = False
        connection_kwargs = dict(self.connection_kwargs)
        connection_kwargs.update(kwargs)
        return redis_class(connection_pool=connection_pool_class(
            service_name, self, **connection_kwargs))
<|endoftext|>