Example #1
0
 def keymap(self):
     k1 = 'key'
     v1 = 'value'
     k2 = 'key2'
     v2 = 'value2'
     m = Map({k1: v1, k2: v2})
     res = m.keymap(lambda a: len(a))
     res.should.have.key(len(k1)).being.equal(v1)
     res.should.have.key(len(k2)).being.equal(v2)
Example #2
0
 def flat_map(self):
     k1 = 'key'
     v1 = 'value'
     k2 = 'key2'
     v2 = 'value2'
     m = Map({k1: v1, k2: v2})
     res = m.flat_map(lambda a, b: Just((a, b)) if a == k1 else Empty())
     res.should.have.key(k1).being.equal(v1)
     res.should_not.have.key(k2)
Example #3
0
 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())
Example #4
0
 def map(self):
     k1 = 'key'
     v1 = 'value'
     k2 = 'key2'
     v2 = 'value2'
     m = Map({k1: v1, k2: v2})
     res = m.map(lambda a: (len(a[0]), len(a[1])))
     res.should.have.key(len(k1)).being.equal(len(v1))
     res.should.have.key(len(k2)).being.equal(len(v2))
Example #5
0
 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())
Example #6
0
 def find(self):
     k1 = 'key'
     v1 = 'value'
     k2 = 'key2'
     v2 = 'value2'
     m = Map({k1: v1, k2: v2})
     m.find(_ == v1).should.equal(Just((k1, v1)))
     m.find_key(_ == k2).should.equal(Just((k2, v2)))
     m.find(_ == 'invalid').should.equal(Empty())
     m.find_key(_ == 'invalid').should.equal(Empty())
Example #7
0
def logger_tree(root):
    from tryp import __, Map
    m = Map(logging.Logger.manager.loggerDict)
    all = m.keyfilter(__.startswith(root))
    return sub_loggers(all, 'tryp')
Example #8
0
 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())
Example #9
0
 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)
Example #10
0
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)
Example #11
0
def decode_dict(value):
    from tryp import Map
    return Map.wrap(value)\
        .keymap(decode)\
        .valmap(decode)
Example #12
0
 def __init__(self, vim: NvimFacade, loop=None) -> None:
     self.vim = vim
     self.loop = loop or asyncio.get_event_loop()
     self.current = Map()  # type: Map[Any, Job]
Example #13
0
class ProcessExecutor(Logging):
    ''' Handler for subprocess execution
    Because python handles signals only on the main thread and
    subprocess notifcations, like their termination, is noticed by
    catching SIG_CHLD, the actual execution of the coroutines spawning
    the subprocess must be done while the main threads's event loop is
    in the running state, so it can relay the signal to the subprocess's
    event loop.
    As this class can be instantiated and used from within another
    thread's event loop, it is also impossible (afaict) to use either
    that loop or the main thread loop, for proc.wait() thus blocks
    indefinitely with a <defunct> process.
    '''

    def __init__(self, vim: NvimFacade, loop=None) -> None:
        self.vim = vim
        self.loop = loop or asyncio.get_event_loop()
        self.current = Map()  # type: Map[Any, Job]

    async def process(self, job: Job):
        return await asyncio.create_subprocess_exec(
            job.exe,
            *job.args,
            stdin=PIPE,
            stdout=PIPE,
            stderr=PIPE,
            cwd=str(job.cwd),
            loop=self.loop,
        )

    async def _execute(self, job: Job):
        try:
            with self._main_event_loop():
                proc = await self.process(job)
                (out, err) = await proc.communicate(job.stdin)
            msg = '{} executed successfully ({}, {})'.format(job, out, err)
            self.log.debug(msg)
            return proc.returncode, out.decode(), err.decode()
        except Exception as e:
            self.log.exception('{} failed with {}'.format(job, repr(e)))
            return -111, '', 'exception: {}'.format(e)

    def run(self, job: Job) -> Future[Result]:
        ''' return values of execute are set as result of the task
        returned by ensure_future(), obtainable via task.result()
        '''
        if self._can_execute(job):
            self.log.debug('executing {}'.format(job))
            task = asyncio.ensure_future(self._execute(job), loop=self.loop)
            task.add_done_callback(job.finish)
            task.add_done_callback(F(self.job_done, job))
            self.current[job.client] = job
        else:
            self.log.error('invalid execution job: {}'.format(job))
            job.cancel('invalid')
        return job.status

    def _can_execute(self, job: Job):
        return job.client not in self.current and job.valid

    def job_done(self, job, status):
        self.log.debug('{} is done with status {}'.format(job, status))
        if job.client in self.current:
            self.current.pop(job.client)

    @property
    def ready(self):
        return self.current.is_empty

    def _main_event_loop(self):
        return (self.vim.main_event_loop() if trypnv.in_vim else
                self._dummy_ctx())

    @contextmanager
    def _dummy_ctx(self):
        yield

    def _run_jobs(self):
        self.current.valmap(lambda job: job.run())

    @property
    def futures(self):
        return self.current.v.map(_.status)

    def await_threadsafe(self, loop):
        async def waiter():
            while not self.ready:
                await asyncio.sleep(0.001)
        loop.run_until_complete(waiter())