예제 #1
0
class EventService(Service):
    def __init__(self, env):
        """event service"""
        super(EventService, self).__init__(env)
        self._channel = Signal('event_channel')

    def subscribe(self, func, event_type=None ):
        '''
        :param func: def func(event_type, **kwarg):
                        pass
        :param event_filter: option
        :return:
        '''
        # sender = event_type or ANY
        # weak = True
        # if isinstance(event_type, basestring):
        #     weak = False
        # self._channel.connect(func, sender, weak)
        sender = event_type or ANY
        self._channel.connect(func, sender)

    def unsubscribe(self, func):
        self._channel.disconnect(func)

    def publish(self, event_type, **kwarg):
        self._channel.send(event_type, **kwarg)
예제 #2
0
파일: teach.py 프로젝트: epsilonxe/python
class CSession:
    def __init__(self, name, start, end):
        self.name = name
        self.uid = uuid.uuid1()
        self.start = datetime.strptime(start, DT_FORMAT)
        self.end = datetime.strptime(end, DT_FORMAT)
        self.on_live = Signal('on-live')
        self.on_end = Signal('on-end')
        self.liveURL = None
        self._teacher = None
        self.popups = []

    @property
    def teacher(self):
        return self._teacher

    @teacher.setter
    def teacher(self, t):
        self.on_live.connect(t.live_recv)
        self.on_end.connect(t.session_end_recv)
        self._teacher = t

    def live(self):
        self.on_live.send(self, msg="hello")

    def liveEnd(self):
        self.on_end.send(self, msg="bye")

    def openLive(self):
        webbrowser.open_new(self.liveURL)
예제 #3
0
class WalkoffSignal(object):
    _signals = {}

    def __init__(self, name, event_type, loggable=True, message=''):
        self.name = name
        self.signal = Signal(name)
        self.event_type = event_type
        if loggable:
            signal_callback = partial(add_entry_to_case,
                                      data='',
                                      event_type=event_type.name,
                                      entry_message=message,
                                      message_name=name)
            self.connect(signal_callback, weak=False)

    def send(self, sender, **kwargs):
        self.signal.send(sender, **kwargs)

    def connect(self, func, weak=True):
        self.signal.connect(func)
        if not weak:
            WalkoffSignal._store_callback(func)
        return func

    @classmethod
    def _store_callback(cls, func):
        """
        Stores callbacks so they aren't garbage collected and the weak references of the signals disappear
        """
        cls._signals[id(func)] = func
예제 #4
0
class WalkoffSignal(object):
    """A signal to send Walkoff data

    The class is a wrapper around a blinker.Signal

    Attributes:
        name (str): The name of the signal
        signal (Signal): The signal object which sends the event and data
        event_type (EventType): The event type of this signal
        is_sent_to_interfaces (bool, optional): Should this event get sent to the interface dispatcher? Defaults to True
        message (str): Human readable message for this event

    Args:
        name (str): The name of the signal
        event_type (EventType): The event type of this signal
        send_to_interfaces (bool, optional): Should this event get sent to the interface dispatcher? Defaults to True
        message (str, optional): Human readable message for this event. Defaults to empty string
    """
    _signals = {}

    def __init__(self, name, event_type, send_to_interfaces=True, message=''):
        self.name = name
        self.signal = Signal(name)
        self.event_type = event_type
        self.is_sent_to_interfaces = send_to_interfaces
        self.message = message

    def send(self, sender, **kwargs):
        """Sends the signal with data

        Args:
            sender: The thing that is sending the signal

        Kwargs:
            data: Additional data to send with the signal
        """
        self.signal.send(sender, **kwargs)

    def connect(self, func, weak=True):
        """A decorator which registers a function as a callback for this signal

        Args:
            func (func): The function to register
            weak (bool, optional): Should a weak reference be used for this connection? Defaults to True

        Returns:
            func: The function connected
        """
        self.signal.connect(func)
        if not weak:
            WalkoffSignal._store_callback(func)
        return func

    @classmethod
    def _store_callback(cls, func):
        """
        Stores callbacks so they aren't garbage collected and the weak references of the signals disappear
        """
        cls._signals[id(func)] = func
예제 #5
0
파일: events.py 프로젝트: iadgov/WALKOFF
class WalkoffSignal(object):
    """A signal to send Walkoff data

    The class is a wrapper around a blinker.Signal

    Attributes:
        name (str): The name of the signal
        signal (Signal): The signal object which sends the event and data
        event_type (EventType): The event type of this signal
        is_sent_to_interfaces (bool, optional): Should this event get sent to the interface dispatcher? Defaults to True
        message (str): Human readable message for this event

    Args:
        name (str): The name of the signal
        event_type (EventType): The event type of this signal
        send_to_interfaces (bool, optional): Should this event get sent to the interface dispatcher? Defaults to True
        message (str, optional): Human readable message for this event. Defaults to empty string
    """
    _signals = {}

    def __init__(self, name, event_type, send_to_interfaces=True, message=''):
        self.name = name
        self.signal = Signal(name)
        self.event_type = event_type
        self.is_sent_to_interfaces = send_to_interfaces
        self.message = message

    def send(self, sender, **kwargs):
        """Sends the signal with data

        Args:
            sender: The thing that is sending the signal

        Kwargs:
            data: Additional data to send with the signal
        """
        self.signal.send(sender, **kwargs)

    def connect(self, func, weak=True):
        """A decorator which registers a function as a callback for this signal

        Args:
            func (func): The function to register
            weak (bool, optional): Should a weak reference be used for this connection? Defaults to True

        Returns:
            func: The function connected
        """
        self.signal.connect(func)
        if not weak:
            WalkoffSignal._store_callback(func)
        return func

    @classmethod
    def _store_callback(cls, func):
        """
        Stores callbacks so they aren't garbage collected and the weak references of the signals disappear
        """
        cls._signals[id(func)] = func
예제 #6
0
파일: events.py 프로젝트: Chewvala/WALKOFF
class Event:
    def __init__(self, callback, name=''):
        self.signal = Signal()
        self.name = name
        self.callback = callback(
            name
        )  # needed b/c blinker cannot use weak references for callbacks
        self.signal.connect(self.callback)

    def send(self, sender):
        self.signal.send(sender)
예제 #7
0
class Foo:

    def __init__(self):
        self.on_hit = Signal()
        self.on_hit.connect(self.handle_hit)
        self.on_hit.connect(self.handle_again)

    def handle_hit(self, *args):
        print('hit!', self, args)

    def handle_again(self, *args):
        print('hit again!', self, args)
예제 #8
0
def anonymous_signal():
    """
    匿名信号的例子

    :return: no return
    """
    # 定义一个信号
    animal_signal = Signal()
    # 信号注册一个接收者
    animal_signal.connect(animal)
    # 发送信号
    animal_signal.send("anonymous")
예제 #9
0
class cons_d2c(object):
    '''包含约束的d2c类'''
    def __init__(self, d, constrains, init=True):
        '''
        Parameters:
            d: 一个字典
            constrains: 约束条目,每个元素都为一个三元元组(deps, func, target),例如,
                (
                    (('k1.k11','k1.k12'),   (lambda v1,v2:v1+v2),   'k2'       ),
                    (('k3','k4'),           (lambda v1,v2:(v1,v2)), ('k5','k6')),
                )
            init: bool, 是否使用约束条件初始化传入的字典
        '''
        self.__d2c_instance = d2c(d)
        self.sig = Signal()
        self.constrains = constrains
        self.subscribers = self.__init_cons(constrains, init=init)

    def __init_cons(self, constrains, init=True):
        subscribers = []
        for deps, func, target in constrains:
            subs = _subscrib(deps, func, target, self)
            subscribers.append(subs)
            for _k in deps:
                self.sig.connect(subs, sender=_k)
        if init:
            deps_set = set()
            for deps, func, target in constrains:
                deps_set.update(deps)
            for dep in deps_set:
                self.sig.send(dep)
        return subscribers

    def set(self, k, v, check=True):
        _changed = self.__d2c_instance.set(k, v, check=check)
        if _changed:
            self.sig.send(k)
        return _changed

    def get(self, k):
        return self.__d2c_instance.get(k)

    def update(self, d, check=True):
        k_changed = []
        for k, v in d.items():
            _changed = self.__d2c_instance.set(k, v, check=check)
            if _changed:
                k_changed.append(k)
        for _k in k_changed:
            self.sig.send(_k)

    def __getattr__(self, item):
        return getattr(self.__d2c_instance, item)
예제 #10
0
class Settings():
    """Make multiple *Value instances accessible as a dict with their name as key"""
    def __init__(self, *values):
        # _values and _values_dict hold the same instances; the list is to
        # preserve order and the dict is for fast access via __getitem__
        self._values = []
        self._values_dict = {}
        self._on_change = Signal()
        self.load(*values)

    def add(self, value):
        """Add `value` to collection"""
        self._values.append(value)
        self._values_dict[value.name] = value

    def load(self, *values):
        """Add multiple `values` to collection"""
        for v in values:
            self.add(v)

    @property
    def values(self):
        """Iterate over collected values"""
        yield from self._values

    @property
    def names(self):
        """Iterate over values' `name` properties"""
        yield from self._values_dict.keys()

    def on_change(self, callback, autoremove=True):
        """
        Run `callback` every time a value changes

        `callback` gets the value instances as the only argument.

        If `autoremove` is True, stop calling `callback` once it is garbage
        collected.
        """
        self._on_change.connect(callback, weak=autoremove)

    def __getitem__(self, name):
        return self._values_dict[name]

    def __setitem__(self, name, value):
        self._values_dict[name].value = value
        self._on_change.send(self._values_dict[name])

    def __contains__(self, name):
        return name in self._values_dict
예제 #11
0
def __construct_logging_signal(event_type, message_name, entry_message):
    """
    Constructs a blinker Signal to log an event to the log database. Note: The returned callback must be stored to a
    module variable for the signal to work.
    :param event_type (str): Type of event whcih is logged 'Workflow, Step, etc.'
    :param message_name (str): Name of message
    :param entry_message (str): More detailed message to log
    :param data (str): Extra information
    :return: (signal, callback): The constructed blinker signal and its associated callback.
    """
    signal = Signal(message_name)
    signal_callback = partial(__add_entry_to_case_wrapper,
                              data='',
                              event_type=event_type,
                              message_name=message_name,
                              entry_message=entry_message)
    signal.connect(signal_callback)
    return signal, signal_callback  # need to return a tuple and save it to avoid weak reference
예제 #12
0
파일: settings.py 프로젝트: Alterrien/stig
class Settings():
    """Dict of ordered Value objects"""
    def __init__(self, *values):
        # _values and _values_dict hold the same instances; the list is to
        # preserve order and the dict is for access via __getitem__, etc.
        self._values = []
        self._values_dict = {}
        self._on_change = Signal()
        self.load(*values)

    def add(self, value):
        self._values.append(value)
        self._values_dict[value.name] = value

    def load(self, *values):
        for v in values:
            self.add(v)

    @property
    def names(self):
        yield from self._values_dict.keys()

    @property
    def values(self):
        yield from self._values

    def on_change(self, callback, autoremove=True):
        """Run `callback` every time a value changes with the value

        If `autoremove` is True, stop calling callback once it is garbage
        collected.
        """
        self._on_change.connect(callback, weak=autoremove)

    def __getitem__(self, name):
        return self._values_dict[name]

    def __setitem__(self, name, value):
        self._values_dict[name].set(value)
        self._on_change.send(self._values_dict[name])

    def __contains__(self, name):
        return name in self._values_dict
예제 #13
0
파일: callbacks.py 프로젝트: silky/WALKOFF
def __construct_logging_signal(event_type, message_name, entry_message):
    """Constructs a blinker Signal to log an event to the log database.

    Note:
        The returned callback must be stored to a module variable for the signal to work.
        
    Args:
        event_type (str): Type of event which is logged 'Workflow, Action, etc.'
        message_name (str): Name of message
        entry_message (str): More detailed message to log
        
    Returns:
        (signal, callback): The constructed blinker signal and its associated callback.
    """
    signal = Signal(message_name)
    signal_callback = partial(add_entry_to_case,
                              data='',
                              event_type=event_type,
                              entry_message=entry_message,
                              message_name=message_name)
    signal.connect(signal_callback)
    return signal, signal_callback  # need to return a tuple and save it to avoid weak reference
예제 #14
0
class Settings(abc.Mapping):
    """Specialized mapping for *Value instances"""
    def __init__(self):
        self._defaults = {}
        self._values = {}
        self._constructors = {}
        self._descriptions = {}
        self._signals = {}
        self._global_signal = Signal()

    def add(self, name, constructor, default, description=None):
        """
        Add new setting

        name:        Identifier for this setting
        constructor: Callable that takes one argument and returns a new value
                     for this setting
        default:     Initial and default value
        description: What the setting does
        """
        self._constructors[name] = constructor
        self._signals[name] = Signal()
        self._descriptions[name] = description
        self[name] = default
        self._defaults[name] = self[name]
        self._global_signal.send(self, name=name, value=self[name])

    def reset(self, name):
        """Reset setting `name` to default/initial value"""
        self[name] = self._defaults[name]

    def default(self, name):
        """Return settings default/initial value"""
        return self._defaults[name]

    def description(self, name):
        """Return setting's description"""
        return self._descriptions[name]

    def syntax(self, name):
        """Return setting's description"""
        return self._constructors[name].syntax

    def validate(self, name, value):
        """Pass `value` to `name`'s constructor and return the result"""
        if not isinstance(value, str) and isinstance(value, abc.Iterable):
            return self._constructors[name](*value)
        else:
            return self._constructors[name](value)

    def on_change(self, callback, name=None, autoremove=True):
        """
        Run `callback` every time a value changes

        If `name` is None, run `callback` if any signal changes.

        The signature of `callback` must be: (settings, name, value)

        If `autoremove` is True, stop calling `callback` once it is garbage
        collected.
        """
        if name is None:
            self._global_signal.connect(callback, weak=autoremove)
        else:
            self._signals[name].connect(callback, weak=autoremove)

    def __getitem__(self, name):
        return self._values[name]

    def __setitem__(self, name, value):
        value_ = self.validate(name, value)
        self._values[name] = value_
        self._global_signal.send(self, name=name, value=value_)
        self._signals[name].send(self, name=name, value=value_)

    def __contains__(self, name):
        return name in self._constructors

    def __iter__(self):
        return iter(self._constructors)

    def __len__(self):
        return len(self._constructors)
예제 #15
0
class ValueBase():
    """Base class for *Value classes"""

    type = None  # Something like int, str or tuple
    typename = 'anything'  # User-readable explanation

    def __init__(self, name, *, default=None, description=None):
        self._name = str(name)
        self._description = str(description) or 'No description available'
        self._on_change = Signal()
        self._default = self._value = self._prev_value = None
        if default is not None:
            initial_value = self.convert(default)
            self.validate(initial_value)
            self._value = initial_value
            self._default = self._value
        log.debug('Initialized ValueBase: %s=%r', self._name, self._value)

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = str(name)

    @property
    def description(self):
        return self._description

    @description.setter
    def description(self, description):
        self._description = str(description)

    @property
    def value(self):
        """Convenience property for `get` method"""
        return self.get()

    @value.setter
    def value(self, value):
        self.set(value)

    @property
    def default(self):
        """Convenience property for `get_default` method"""
        return self.get_default()

    @default.setter
    def default(self, default):
        self.set_default(default)

    def get(self):
        """Return current value"""
        return self._value

    def set(self, value):
        """
        Set current value

        When setting this property, callbacks connected to `on_change` get the
        current value (after validation).  If one of the callbacks raises
        ValueError, the change is reverted and ValueError is raised.
        """
        # convert() and validate() may raise ValueError
        new_value = self.convert(value)
        self.validate(new_value)

        # Set new value
        prev_value = self._value
        self._value = new_value

        # Callbacks can revert the change by raising ValueError
        try:
            self._on_change.send(self)
        except ValueError:
            self._value = prev_value
            raise
        else:
            self._prev_value = prev_value

    def get_default(self):
        """Return default value or `None` if no default is specified"""
        return self._default

    def set_default(self, default):
        """
        Change default value

        Raise ValueError if `default` doesn't pass through `convert` and
        `validate` methods.
        """
        try:
            new_default = self.convert(default)
            self.validate(new_default)
        except ValueError as e:
            raise ValueError('{} = {}: {}'.format(self.name,
                                                  self.string(default), e))
        else:
            self._default = new_default

    def prev_value(self):
        return self._prev_value

    def reset(self):
        """Reset current value back to default"""
        self.value = self.default

    def validate(self, value):
        """
        Raise ValueError if `value` is not valid

        The default implementation checks if `value` is of the type specified in
        the class attribute `type`.  If `type` is None (the default), all values
        valid.

        Additionally, subclasses may check for things like max/min length/number
        (see `StringValue` and `NumberValue` for examples).
        """
        if self.type is not None and not isinstance(value, self.type):
            raise ValueError('Not a {}'.format(self.typename))

    def convert(self, value, *args, **kwargs):
        """
        Try to convert value to correct type before validation (e.g. str->int)

        Raise ValueError if impossible
        """
        if self.type is None or isinstance(value, self.type):
            return value
        try:
            return self.type(value, *args, **kwargs)
        except Exception:
            raise ValueError('Not a {}'.format(self.typename))

    def string(self, value=None, default=False):
        """
        Return prettily stringified value

        value: The value to stringify or `None` to use `value` property
        default: Whether to stringify current or default value (setting this to
                 True ignores the value argument)

        If possible, use `convert` to parse `value` before stringifying it.

        If `value` is invalid, `str(value)` or something similar should be
        returned so we can provide pretty error messages.  This method must not
        raise any exceptions.
        """
        if default:
            value = self.default
        elif value is not None:
            try:
                value = self.convert(value)
            except ValueError as e:
                value = value
        else:
            value = self.value

        # Display `None` as something more user-readable
        text = UNSPECIFIED if value is None else str(value)

        if not text or (text[0] == ' ' or text[-1] == ' '):
            return repr(text)
        else:
            return text

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

    def __repr__(self):
        v = self.value
        return '%s=%s' % (self.name, UNSPECIFIED if v is None else repr(v))

    def __eq__(self, other):
        if isinstance(other, type(self)):
            return self.value == other.value
        else:
            return self.value == other

    def __ne__(self, other):
        return not self.__eq__(other)

    def __gt__(self, other):
        if isinstance(other, type(self)):
            return self.value > other.value
        try:
            return self.value > other
        except TypeError:
            return NotImplemented

    def __lt__(self, other):
        if isinstance(other, type(self)):
            return self.value < other.value
        try:
            return self.value < other
        except TypeError:
            return NotImplemented

    def __ge__(self, other):
        if isinstance(other, type(self)):
            return self.value >= other.value
        try:
            return self.value >= other
        except TypeError:
            return NotImplemented

    def __le__(self, other):
        if isinstance(other, type(self)):
            return self.value <= other.value
        try:
            return self.value <= other
        except TypeError:
            return NotImplemented

    def on_change(self, callback, autoremove=True):
        """
        Pass this object to `callback` every time its value changes

        `callback` may raise ValueError to revert the change (see `set`).

        If `autoremove` is True, stop calling callback once it is garbage
        collected.
        """
        self._on_change.connect(callback, weak=autoremove)
예제 #16
0
class BaseProcess(object):
    def __init__(self, new_func=None, total_count=None, *args, **kwargs):
        self.func = new_func
        self.total_count = total_count
        self.count = 0
        self.task_queue = []
        self.process_list = []
        self.process_map = {}
        self.task_map = {}
        self.args = args
        self.kwargs = kwargs
        signal.signal(signal.SIGWINCH, self.action)
        # self.window = curses.initscr()
        self.add_task_signal = Signal(1000)
        self.count_signal = Signal(1001)
        self.reg_task_signal()
        self.reg_count_sinal()

    def reg_count_sinal(self):
        if not self.total_count:
            return
        else:
            self.count_signal.connect(self.process_count)

    def reg_task_signal(self):
        self.add_task_signal.connect(self.process_func)

    def create_new_task(self, work_num=1):
        for i in range(work_num):
            self.task_queue.append((self.func, self.args, self.kwargs))
            self.add_task_signal.send()
        if self.total_count:
            for i in range(self.total_count):
                self.count_signal.send()
        self.process_map = {x: 0 for x in range(work_num)}



    def make_func(self):
        def add_count_deco(self, func):
            def wrapper(func):
                @functools.wraps(func)
                def _wrapper(*args, **kwargs):
                    func(*args, **kwargs)
                    self.count += 1
                return _wrapper

            return wrapper
        @add_count_deco(self,self.func)
        def _make_func(*args,**kwargs):
            pass
        return _make_func

    def process_func(self, sender):
        while self.task_queue != []:
            self.task_queue.pop()
            process = multiprocessing.Process(target=self.func, args=self.args, kwargs=self.kwargs)
            process.start()

    def process_count(self, sender):
        self.count += 1
        bar_length = 20
        percent = float(self.count * 1.0 / self.total_count)
        hashes = '#' * int(percent * bar_length)
        spaces = ' ' * (bar_length - len(hashes))
        multiprocessing.Process(
            target=sys.stdout.write("\rPercent: [%s] %d%%" % (hashes + spaces, percent * 100))).start()

    def action(self):
        key = self.window.getch()
        if key == ord('p'):
            pass
        if key == ord('r'):
            pass

    def start(self):
        while True:
            self.action()
예제 #17
0
class Settings(abc.Mapping):
    """Typed user configuration settings"""

    def __init__(self):
        self._defaults = {}
        self._values = {}
        self._constructors = {}
        self._getters = defaultdict(lambda: None)
        self._setters = defaultdict(lambda: None)
        self._descriptions = {}
        self._signals = defaultdict(lambda: Signal())
        self._global_signal = Signal()

    def add(self, name, constructor, default, description=None, getter=None, setter=None):
        """
        Add new setting

        name:        Identifier for this setting
        constructor: Callable that takes one argument and returns a new value
                     for this setting
        default:     Initial and default value
        description: What the setting does
        getter:      Callable with no arguments that returns the value
        setter:      Callable with one argument that sets the value
        """
        self._constructors[name] = constructor
        self._descriptions[name] = description
        value_ = self.validate(name, default)
        self._values[name] = value_
        self._defaults[name] = value_
        if getter:
            self._getters[name] = getter
        if setter:
            self._setters[name] = setter
        self._global_signal.send(self, name=name, value=value_)

    def reset(self, name):
        """Reset setting `name` to default/initial value"""
        self[name] = self._defaults[name]

    def default(self, name):
        """Return setting's default/initial value"""
        return self._defaults[name]

    def description(self, name):
        """Return setting's description"""
        return self._descriptions[name]

    def syntax(self, name):
        """Return setting's description"""
        return self._constructors[name].syntax

    def validate(self, name, value):
        """Pass `value` to `name`'s constructor and return the result"""
        if not isinstance(value, str) and isinstance(value, abc.Iterable):
            return self._constructors[name](*value)
        else:
            return self._constructors[name](value)

    def on_change(self, callback, name=None, autoremove=True):
        """
        Run `callback` every time a value changes

        If `name` is None, run `callback` after every change.

        The signature of `callback` must be: (settings, name, value)

        If `autoremove` is True, stop calling `callback` once it is garbage
        collected.
        """
        if name is None:
            self._global_signal.connect(callback, weak=autoremove)
        else:
            self._signals[name].connect(callback, weak=autoremove)

    @property
    def as_dict(self):
        # NOTE 1: The "id" key is important because filters (including
        #         SettingFilter) expect it.  This requirement can possibly
        #         removed but it probably means rewriting lots of filter tests.
        # NOTE 2: The "validate" lambda MUST store the value of `name`,
        #         otherwise `self.validate` always gets called with the for
        #         loop's last `name` value.
        return {name:{'id': name,
                      'value': self[name],
                      'default': self.default(name),
                      'description': self.description(name),
                      'syntax': self.syntax(name),
                      'validate': lambda v, n=name: self.validate(n, v)}
                for name,value in self.items()}

    def __getitem__(self, name):
        getter = self._getters[name]
        if getter is not None:
            return self.validate(name, getter())
        else:
            return self._values[name]

    def __setitem__(self, name, value):
        value_ = self.validate(name, value)
        setter = self._setters[name]
        if setter is not None:
            setter(value_)
        else:
            self._values[name] = value_
        self._global_signal.send(self, name=name, value=value_)
        self._signals[name].send(self, name=name, value=value_)

    def __contains__(self, name):
        return name in self._constructors

    def __iter__(self):
        return iter(self._constructors)

    def __len__(self):
        return len(self._constructors)
예제 #18
0
파일: settings.py 프로젝트: Alterrien/stig
class ValueBase():
    """Name:value pair with validation, default value and description"""
    def __init__(self, name, default, description='No description available'):
        """Create new value"""
        self.__name = name
        self.__value = self.__default = self.convert(default)
        self.__description = description
        self.__on_change = Signal()

    # Must be set by derived classes
    type = NotImplemented
    typename = '<NOT IMPLEMENTED>'

    @property
    def value(self):
        return self.__value

    @property
    def name(self):
        return self.__name

    @property
    def default(self):
        return self.__default

    @property
    def description(self):
        return self.__description

    def on_change(self, callback, autoremove=True):
        """Pass this object to `callback` every time its value changes

        `callback` may raise ValueError to revert the change (see `set`).

        If `autoremove` is True, stop calling callback once it is garbage
        collected.
        """
        self.__on_change.connect(callback, weak=autoremove)

    def set(self, value):
        """Change value if valid, reset to default if None

        Callbacks connected to `on_change` are passed this object every time a
        value is changed.  If a callback raises ValueError, the change is
        reverted and a ValueError is raised.
        """
        if value is None:
            value = self.__default
        try:
            new_value = self.convert(value)
            self.validate(new_value)
        except ValueError as e:
            raise ValueError('{} = {}: {}'.format(self.name, self.str(value),
                                                  e))
        else:
            prev_value = self.__value
            self.__value = new_value
            # Callbacks can revert the change by raising ValueError
            try:
                self.__on_change.send(self)
            except ValueError as e:
                self.__value = prev_value
                raise ValueError('{} = {}: {}'.format(self.name,
                                                      self.str(value), e))

    def get(self):
        """Return current value"""
        return self.value

    def validate(self, value):
        """Raise ValueError if value is not valid"""
        if not isinstance(value, self.type):
            raise ValueError('Not a {}'.format(self.typename))

    def convert(self, value):
        """Try to convert value to correct type before validation (e.g. str->int)

        Raise ValueError if impossible"""
        if isinstance(value, self.type):
            return value
        try:
            if isinstance(value, abc.Iterable):
                return self.type(''.join(value))
            else:
                return self.type(value)
        except Exception:
            raise ValueError('Not a {}'.format(self.typename))

    def __eq__(self, other):
        return self.value == other.value

    def __ne__(self, other):
        return self.value != other.value

    def str(self, value=None, default=False):
        """Return prettily stringified value

        value: None to return current value, or specific value
        default: Whether to return current or default value
        """
        if default:
            return str(self.default)
        elif value is not None:
            return str(value)
        else:
            return str(self.value)

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

    def __repr__(self):
        return '{}={!r}'.format(self.name, self.value)
예제 #19
0
class SignalEmitter:
    """Class that allows other callables to connect to it listen for signals.

    Callables can be attached to a SignalEmitter via SignalEmitter.connect.
    If the SignalEmitter calls SignalEmitter.signal.send(*args, **kwargs), any
    callables are called with the respective args and kwargs.

    Args:
        initialize_signal: instantiate a blnker.Signal object. If set to False,
            self.Signal needs to be set later on. This could be useful when you
            want a single Signal that is shared by all class instances.
        multiple_senders: Allow to be connected to multiple senders.
            If False, when connected to a second SignalEmitter, the connection
            to the previous SignalEmitter is disconnected

    Note:
        The SignalEmitter has protection against infinite recursions resulting
        from signal emitters calling each other. This is done by keeping track
        of the signal chain. However, it does not protect against infinite
        recursions from signals sent from objects that are not signal emitters.
    """
    # Signal used for connecting to parameter via SignalEmitter.connect method
    signal = None

    def __init__(self,
                 initialize_signal: bool = True,
                 multiple_senders: bool = True):
        self._signal_chain = []
        if initialize_signal:
            self.signal = Signal()

        self._signal_modifiers = {'offset': None, 'scale': None}

        # By default self is not connected to any other SignalEmitter
        self.sender = None

        self.multiple_senders = multiple_senders

    def connect(self,
                receiver,
                update=False,
                offset: float = None,
                scale: float = None):
        """Connect a receiver, which can be another SignalEmitter.

        If a SignalEmitter is passed, the __call__ method is invoked.

        Args:
            receiver: Receiver to be connected to this SignalEmitter's signal.
            offset: Optional offset to apply to emitted value
            scale: Optional scale to apply to emitted value

        Note:
            If offset or scale is provided, the emitted value should be a number.
            If an emitted signal contains 'value' as kwarg, this will be
            modified. Otherwise the first arg (sender) will be modified.
        """
        if self.signal is None:
            self.signal = Signal()

        if isinstance(receiver, SignalEmitter):
            # Remove any previous sender if multiple_senders is False
            if not receiver.multiple_senders and receiver.sender is not None:
                receiver.sender.disconnect(receiver)

            receiver._signal_modifiers['offset'] = offset
            receiver._signal_modifiers['scale'] = scale
            self.signal.connect(receiver._signal_call)
            receiver.sender = self

        else:
            self.signal.connect(receiver)

        if update:
            # Update callable with current value
            value = self()
            if scale is not None:
                if callable(scale):
                    scale = scale(self)
                value *= scale
            if offset is not None:
                if callable(offset):
                    offset = offset(self)
                value += offset

            receiver(value)

    def disconnect(self, callable):
        """disconnect a callable from a SignalEmitter.

        Note:
            Does not raise error if callable is not connected in the first place
            """
        if isinstance(callable, SignalEmitter):
            callable = callable._signal_call

        if getattr(self, 'signal', None) is not None:
            for receiver_ref in list(self.signal.receivers.values()):
                receiver = receiver_ref()
                if receiver == callable:
                    self.signal.disconnect(callable)

    def _signal_call(self, sender, *args, **kwargs):
        """Method that is called instead of standard __call__ for SignalEmitters

        This method ensures that the actual __call__ is only invoked if this has
        not previously been done during the signal chain.
        """
        if self not in self._signal_chain:
            value = kwargs.get('value', sender)

            # If any modifier is set,
            if self._signal_modifiers['scale'] is not None:
                scale = self._signal_modifiers['scale']
                if callable(scale):
                    scale = scale(self.sender)
                value *= scale

            if self._signal_modifiers['offset'] is not None:
                offset = self._signal_modifiers['offset']
                if callable(offset):
                    offset = offset(self.sender)
                value += offset

            if 'value' in kwargs:
                kwargs['value'] = value
            else:
                sender = value
            return self(sender,
                        *args,
                        signal_chain=self._signal_chain,
                        **kwargs)
예제 #20
0
파일: teach.py 프로젝트: epsilonxe/python
class TSys:
    gmaps = googlemaps.Client(key=GG_APIKEY)

    def __init__(self, name):
        self.name = name
        self.courses = []
        self.cats = set()
        self.students = set()
        self.teachers = set()

        self.on_new_course = Signal('on-new-course')
        self.on_new_student = Signal('on-new-student')
        self.on_new_teacher = Signal('on-new-teacher')

    def commit(self):
        DbManager.commit(self.courses)
        DbManager.commit(self.teachers)
        DbManager.commit(self.students)
        return True

    def addCat(self, cat):
        for c in cat:
            self.cats.add(c)

    def direction(self, *args, **kwargs):
        return TSys.gmaps.directions(*args, **kwargs)

    def geocode(self, *args, **kwargs):
        return TSys.gmaps.geocode(*args, **kwargs)

    def _connectSignal(self, o):
        self.on_new_teacher.connect(o.newTeacherRecv)
        self.on_new_student.connect(o.newTeacherRecv)
        self.on_new_course.connect(o.newCourseRecv)

    def newTeacher(self, t):
        self.on_new_teacher.send(self, teacher=t)

    def newStudent(self, t):
        self.on_new_student.send(self, student=t)

    def newCourse(self, t):
        self.on_new_course.send(self, course=t)

    def addTeacher(self, t):
        self._connectSignal(t)

        found = [x for x in self.teachers if t.email == x.email]
        if len(found) == 0:
            self.teachers.add(t)
            self.newTeacher(t)
            return True
        else:
            return False

    def addStudent(self, s):
        self._connectSignal(s)

        found = [x for x in self.students if s.email == x.email]
        if len(found) == 0:
            self.students.add(s)
            self.newStudent(s)
            return True
        else:
            return False

    def studentLogin(self, email, passwd):
        found = [x for x in self.students if email == x.email]
        if len(found) > 0:
            if found[0].verify_password(passwd) == True:
                return found[0]
            else:
                return False
        else:
            return False

    def teacherLogin(self, email, passwd):
        found = [x for x in self.teachers if email == x.email]
        if len(found) > 0:
            if found[0].verify_password(passwd) == True:
                return found[0]
            else:
                return False
        else:
            return False

    def createCourse(self, course):
        self.courses.append(course)
        self.newCourse(course)

    def filter(self, cats):
        results = []
        for c in cats:
            results.extend([x for x in self.courses if c in x.cats])
        return results

    def find_students(self, filters):
        results = []
        for s in self.students:
            match = True
            for k in filters.keys():
                if k == "subject":
                    match = filters[k] in s.subjects
                if k == "location":
                    match = filters[k] in s.locations
                if k == "per_hour":
                    if s.max_per_hour >= filters[k]:
                        match = True
                    else:
                        match = False
                if match == False:
                    break
            if match == True:
                results.append(s)
        return results