def add(self): key = 'key' val = 'value' k2 = 'key2' v2 = 'value2' m = Map({key: val}) m2 = m + (k2, v2) m2.get(k2).should.equal(Just(v2)) m.get(k2).should.equal(Empty())
def add_multi(self): key = 'key' val = 'value' k2 = 'key2' v2 = 'value2' m = Map({key: val}) m2 = m ** Map({k2: v2}) m2.get(k2).should.equal(Just(v2)) m.get(k2).should.equal(Empty())
def get(self): key = 'key' val = 'value' m = Map({key: val}) m.get(key).should.equal(Just(val)) m.get(key + key).should.equal(Empty())
class Machine(Logging): _data_type = Data def __init__(self, name: str, parent: "Machine" = None) -> None: self.name = name self.parent = Maybe(parent) self._setup_handlers() def _setup_handlers(self): handlers = inspect.getmembers(self, lambda a: hasattr(a, _machine_attr)) handler_map = List.wrap(handlers).smap(Handler.create).map(lambda a: (a.message, a)) self._message_handlers = Map(handler_map) @property def _default_handler(self): return Handler("unhandled", None, self.unhandled) def process(self, data: Data, msg) -> TransitionResult: self.prepare(msg) handler = self._resolve_handler(msg) result = self._execute_transition(handler, data, msg) return self._dispatch_transition_result(data, result) def prepare(self, msg): pass def _dispatch_transition_result(self, data, result): return result / self._process_result(data) | TransitionResult.empty(data) def loop_process(self, data, msg): sender = lambda z, m: z.accum(self.loop_process(z.data, m)) return self.process(data, msg).fold(sender) def _resolve_handler(self, msg): return self._message_handlers.get(type(msg)).get_or_else(lambda: self._default_handler) def _execute_transition(self, handler, data, msg): try: return handler.run(data, msg) except Exception as e: return self._handle_transition_error(handler, msg, e) def _handle_transition_error(self, handler, msg, e): err = 'transition "{}" failed for {} in {}' self.log.exception(err.format(handler.name, msg, self.name)) if tryp.development: raise TransitionFailed() from e return Empty() @curried def _process_result(self, old_data: Data, result) -> TransitionResult: if isinstance(result, Coroutine): return CoroTransitionResult(data=old_data, coro=result) elif isinstance(result, TransitionResult): return result elif isinstance(result, self._data_type): return TransitionResult.empty(result) elif isinstance(result, Message) or not is_seq(result): result = List(result) datas, rest = List.wrap(result).split_type(self._data_type) strict, rest = rest.split_type(Message) coro, rest = rest.split(iscoroutine) msgs = strict + coro.map(Coroutine).map(_.pub) if rest: tpl = "invalid transition result parts in {}: {}" msg = tpl.format(self.name, rest) if tryp.development: raise MachineError(msg) else: self.log.error(msg) new_data = datas.head | old_data return self._create_result(new_data, msgs) def _create_result(self, data, msgs): pub, resend = msgs.split_type(Publish) pub_msgs = pub.map(_.message) return StrictTransitionResult(data=data, pub=pub_msgs, resend=resend) @may def unhandled(self, data: Data, msg: Message): pass def _command_by_message_name(self, name: str): msg_name = camelcaseify(name) return self._message_handlers.find_key(lambda a: a.__name__ in [name, msg_name]) def command(self, name: str, args: list): return ( self._command_by_message_name(name) .map(lambda a: StateCommand(a[0])) .map(_.call("dispatch", self, args)) .or_else(F(self._invalid_command, name)) ) def _invalid_command(self, name): self.log.error('plugin "{}" has no command "{}"'.format(self.name, name)) return Empty() @may_handle(NvimIOTask) def message_nvim_io(self, data: Data, msg): msg.io.unsafe_perform_io(self.vim) @may_handle(RunTask) def message_run_task(self, data: Data, msg): success = lambda r: r if isinstance(r, Message) else None return msg.task.unsafe_perform_sync().cata(F(Error) >> _.pub, success) @may_handle(DataTask) def message_data_task(self, data: Data, msg): return either_msg(msg.cons(Task.now(data)).unsafe_perform_sync()) def bubble(self, msg): self.parent.cata(_.bubble, lambda: self.send)(msg)