Beispiel #1
0
 def __del__(self):
     self._pypy_issue2786_workaround.discard(self._coroutine)
     if getcoroutinestate(self._coroutine) is CORO_CREATED:
         # Never started, nothing to clean up, just suppress the "coroutine
         # never awaited" message.
         self._coroutine.close()
     if getcoroutinestate(
             self._coroutine) is CORO_SUSPENDED and not self._closed:
         if self._finalizer is not None:
             self._finalizer(self)
         else:
             # Mimic the behavior of native generators on GC with no finalizer:
             # throw in GeneratorExit, run for one turn, and complain if it didn't
             # finish.
             thrower = self.athrow(GeneratorExit)
             try:
                 thrower.send(None)
             except (GeneratorExit, StopAsyncIteration):
                 pass
             except StopIteration:
                 raise RuntimeError("async_generator ignored GeneratorExit")
             else:
                 raise RuntimeError(
                     "async_generator {!r} awaited during finalization; install "
                     "a finalization hook to support this, or wrap it in "
                     "'async with aclosing(...):'".format(
                         self.ag_code.co_name))
             finally:
                 thrower.close()
async def test_normal_exit2(nursery, ed):
    '''nursery.start_soon() instead of nursery.start()'''
    from inspect import (
        getcoroutinestate, CORO_CREATED, CORO_SUSPENDED, CORO_CLOSED,
    )
    import trio
    import asynckivy as ak
    from asynckivy.compatibility.trio import run_coro_under_trio
    
    async def ak_func():
        await ak.event(ed, 'on_test')
    ak_coro = ak_func()
    async def trio_func():
        await run_coro_under_trio(ak_coro)
        nonlocal done; done = True

    done = False
    nursery.start_soon(trio_func)
    assert getcoroutinestate(ak_coro) == CORO_CREATED
    assert not done
    await trio.sleep(.01)
    assert getcoroutinestate(ak_coro) == CORO_SUSPENDED
    assert not done
    ed.dispatch('on_test')
    assert getcoroutinestate(ak_coro) == CORO_CLOSED
    await trio.sleep(.01)
    assert done
    def test_cr_await(self):
        @types.coroutine
        def a():
            self.assertEqual(inspect.getcoroutinestate(coro_b), inspect.CORO_RUNNING)
            self.assertIsNone(coro_b.cr_await)
            yield
            self.assertEqual(inspect.getcoroutinestate(coro_b), inspect.CORO_RUNNING)
            self.assertIsNone(coro_b.cr_await)

        async def c():
            await a()

        async def b():
            self.assertIsNone(coro_b.cr_await)
            await c()
            self.assertIsNone(coro_b.cr_await)

        coro_b = b()
        self.assertEqual(inspect.getcoroutinestate(coro_b), inspect.CORO_CREATED)
        self.assertIsNone(coro_b.cr_await)

        coro_b.send(None)
        self.assertEqual(inspect.getcoroutinestate(coro_b), inspect.CORO_SUSPENDED)
        self.assertEqual(coro_b.cr_await.cr_await.gi_code.co_name, 'a')

        with self.assertRaises(StopIteration):
            coro_b.send(None)  # complete coroutine
        self.assertEqual(inspect.getcoroutinestate(coro_b), inspect.CORO_CLOSED)
        self.assertIsNone(coro_b.cr_await)
Beispiel #4
0
 def a():
     self.assertEqual(inspect.getcoroutinestate(coro_b),
                      inspect.CORO_RUNNING)
     self.assertIsNone(coro_b.cr_await)
     yield
     self.assertEqual(inspect.getcoroutinestate(coro_b),
                      inspect.CORO_RUNNING)
     self.assertIsNone(coro_b.cr_await)
Beispiel #5
0
 def __del__(self):
     if getcoroutinestate(self._coroutine) is CORO_CREATED:
         # Never started, nothing to clean up, just suppress the "coroutine
         # never awaited" message.
         self._coroutine.close()
     if getcoroutinestate(self._coroutine) is CORO_SUSPENDED:
         # This exception will get swallowed because this is __del__, but
         # it's an easy way to trigger the print-to-console logic
         raise RuntimeError(
             "partially-exhausted async_generator garbage collected")
Beispiel #6
0
def format_execution_point(coro):
    if asyncio.iscoroutine(coro) or inspect.isasyncgen(coro):
        if inspect.iscoroutine(coro):
            t = 'coroutine'
            s = inspect.getcoroutinestate(coro)
            c = coro.cr_code
            f = coro.cr_frame
        elif inspect.isgenerator(coro):
            t = 'generator'
            s = inspect.getgeneratorstate(coro)
            c = coro.gi_code
            f = coro.gi_frame
        elif inspect.isasyncgen(coro):
            t = 'async_generator'
            s = getasyncgenstate(coro)
            f = coro.ag_frame
            c = coro.ag_code
        else:
            return f"(unsupported coroutine type {type(coro)!r})"
        ref = f'{c.co_filename}:{c.co_firstlineno}:{c.co_name}'
        if s.endswith('CLOSED'):
            return f'{t} {ref} just finished'
        elif s.endswith('SUSPENDED'):
            return f'{t} {ref} stopped at line {f.f_lineno}'
        else:
            assert False, f'Unexpected state {s} for {coro!r})'
    else:
        return f"(can't get execution point for {coro!r})"
Beispiel #7
0
                def body(state):
                    if inspect.getcoroutinestate(x) == inspect.CORO_CLOSED:
                        _x = call()
                    else:
                        _x = x

                    next_ = lambda: _x.send(None)
                    while True:
                        try:
                            future = next_()
                        except StopIteration as e:
                            val = e.value
                            break
                        else:
                            assert isinstance(future, cls)
                            try:
                                val, state = future._f(state)
                            except Exception as e:
                                # We need an intermediate variable here, since
                                # "e" is not really bound in this scope.
                                excep = e
                                next_ = lambda: _x.throw(excep)
                            else:
                                next_ = lambda: _x.send(val)

                    if isinstance(val, cls):
                        return val._f(state)
                    else:
                        return (val, state)
Beispiel #8
0
    def _do_it(self, start_fn, *args):
        if not self._hooks_inited:
            self._hooks_inited = True
            (firstiter, self._finalizer) = get_asyncgen_hooks()
            if firstiter is not None:
                firstiter(self)
            if sys.implementation.name == "pypy":
                self._pypy_issue2786_workaround.add(self._coroutine)

        # On CPython 3.5.2 (but not 3.5.0), coroutines get cranky if you try
        # to iterate them after they're exhausted. Generators OTOH just raise
        # StopIteration. We want to convert the one into the other, so we need
        # to avoid iterating stopped coroutines.
        if getcoroutinestate(self._coroutine) is CORO_CLOSED:
            raise StopAsyncIteration()

        async def step():
            if self.ag_running:
                raise ValueError("async generator already executing")
            try:
                self.ag_running = True
                return await ANextIter(self._it, start_fn, *args)
            except StopAsyncIteration:
                self._pypy_issue2786_workaround.discard(self._coroutine)
                raise
            finally:
                self.ag_running = False

        return step()
Beispiel #9
0
    async def _await_later(self, delay: t.Union[int,
                                                float], task_id: t.Hashable,
                           coroutine: t.Coroutine) -> None:
        """Await `coroutine` after the given `delay` number of seconds."""
        try:
            self._log.info(
                f"Waiting {delay} seconds before awaiting coroutine for #{task_id}."
            )
            await asyncio.sleep(delay)

            # Use asyncio.shield to prevent the coroutine from cancelling itself.
            self._log.info(
                f"Done waiting for #{task_id}; now awaiting the coroutine.")
            await asyncio.shield(coroutine)
        finally:
            # Close it to prevent unawaited coroutine warnings,
            # which would happen if the task was cancelled during the sleep.
            # Only close it if it's not been awaited yet. This check is important because the
            # coroutine may cancel this task, which would also trigger the finally block.
            state = inspect.getcoroutinestate(coroutine)
            if state == "CORO_CREATED":
                self._log.debug(
                    f"Explicitly closing the coroutine for #{task_id}.")
                coroutine.close()
            else:
                self._log.debug(
                    f"Finally block reached for #{task_id}; {state=}")
Beispiel #10
0
async def atomize_cancellation(
        coro: Coroutine[_T],
        *,
        loop: typing.Optional[asyncio.AbstractEventLoop] = None) -> _T:
    if loop is None:
        loop = asyncio.get_event_loop()

    task = loop.create_task(coro)
    was_cancelled = False

    while True:
        try:
            result = await shield(task, loop=loop)
        except asyncio.CancelledError:
            if task.cancelled():
                raise

            if inspect.getcoroutinestate(coro) == inspect.CORO_CREATED:
                task.cancel()
                raise

            was_cancelled = True
        else:
            break

    if was_cancelled:
        asyncio.Task.current_task(loop).cancel()

    return result
Beispiel #11
0
    def monitor(self):
        """Make a curses window to show various jobs managed by the AsyncLoop.

        Note1:
        UNIX ONLY, since this method simply imports `curses` module, which is
        not available for Windows.

        Note2:
        This is an untested method, and I am not sure how to test this.
        """
        import curses
        import inspect

        stdscr = curses.initscr()
        curses.curs_set(0)
        curses.noecho()
        curses.cbreak()
        width_split = curses.COLS // 3 - 1
        win_done = curses.newwin(curses.LINES - 1, width_split, 0, 0)
        win_running = curses.newwin(curses.LINES - 1, width_split, 0,
                                    width_split + 1)
        win_pending = curses.newwin(curses.LINES - 1, width_split, 0,
                                    2 * width_split + 1)
        stdscr.addstr(curses.LINES - 1, 0,
                      'Monitoring started. Press Ctrl+C to stop.')
        stdscr.refresh()
        win_done.addstr(0, 0, 'DONE')
        win_pending.addstr(0, 0, 'PENDING')
        while True:
            try:
                win_done.addstr(1, 0, f'{len(self.done)} jobs done')
                list_done = list(self.done)[:curses.LINES - 3]
                for idx, fut in enumerate(list_done, start=2):
                    fmt_str = f'{id(fut):x} {fut._state}'
                    win_done.addstr(idx, 0, fmt_str)
                win_done.refresh()

                win_running.clear()
                win_running.addstr(0, 0, 'RUNNING')
                win_running.addstr(1, 0,
                                   f'{self.running.qsize()} jobs running')
                list_running = list(self.running.items())[:curses.LINES - 3]
                for idx, (fut, coro) in enumerate(list_running, start=2):
                    coro_state = inspect.getcoroutinestate(coro)
                    fmt_str = f'{id(fut):x} {coro_state}'
                    win_running.addstr(idx, 0, fmt_str)
                win_running.refresh()

                win_pending.clrtoeol()
                win_pending.addstr(1, 0,
                                   f'{self.pending.qsize()} jobs pending')
                win_pending.refresh()
                time.sleep(.1)
            except KeyboardInterrupt:
                break

        curses.nocbreak()
        curses.echo()
        curses.endwin()
def test_cancel_without_start():
    from inspect import getcoroutinestate, CORO_CLOSED
    import asyncgui as ag
    task = ag.Task(ag.sleep_forever())
    task.cancel()
    assert task.cancelled
    assert task._exception is None
    assert getcoroutinestate(task.root_coro) == CORO_CLOSED
Beispiel #13
0
    def validate_coro(self, coro: Awaitable) -> None:
        """Validate that a provided coroutine is actually awaitable"""
        if not inspect.isawaitable(coro):
            raise ConcurrentError(
                f"Provided input was not a coroutine: {coro}")

        if inspect.getcoroutinestate(coro) != inspect.CORO_CREATED:
            raise ConcurrentError(
                f"Provided coroutine has already been fired: {coro}")
Beispiel #14
0
    def whatis(self, arguments):
        """Prints the type of the argument.

        Usage:
            whatis <name>...
        """
        arg = " ".join(arguments["argv"][1:])
        try:
            value = eval(arg, self._obj.curframe.f_globals,
                         self._obj.curframe.f_locals)
        except:  # noqa
            v = sys.exc_info()[1]
            self._ui.printf('*** %R{}%N: {}\n'.format(type(v).__name__, v))
            return

        if inspect.ismodule(value):
            self._ui.print(str(value))
        elif inspect.isasyncgenfunction(value):
            self._ui.print('Async Gen function:', value.__name__,
                           inspect.signature(value))
        elif inspect.isasyncgen(value):
            self._ui.print('Async Gen:', value.__name__,
                           inspect.signature(value))
        elif inspect.iscoroutine(value):
            self._ui.print('Coroutine:', value)
            self._ui.print('    state:', inspect.getcoroutinestate(value))
            if inspect.isawaitable(value):
                self._ui.print('  and awaitable.')
                self._ui.print('  stack:',
                               _coroutine_format_stack(value, complete=False))
        elif inspect.isgenerator(value):
            self._ui.print('Generator:', value)
            self._ui.print('    state:', inspect.getgeneratorstate(value))
            if inspect.isawaitable(value):
                self._ui.print('  and awaitable.')
        elif inspect.iscoroutinefunction(value):
            self._ui.print('Coroutine function:', value.__name__,
                           inspect.signature(value))
        elif inspect.isgeneratorfunction(value):
            self._ui.print('Generator function:', value.__name__,
                           inspect.signature(value))
        elif inspect.isfunction(value):
            self._ui.print('Function:', value.__name__,
                           inspect.signature(value))
        elif inspect.ismethod(value):
            self._ui.print('Method:', value.__name__, inspect.signature(value))
        elif inspect.iscode(value):
            self._ui.print('Code object:', value.co_name)
        elif inspect.isclass(value):
            self._ui.print('Class:', value.__name__)
        elif inspect.ismethoddescriptor(value):
            self._ui.print('Method descriptor:', value.__name__)
        elif inspect.isdatadescriptor(value):
            self._ui.print('Data descriptor:', value.__name__)
        # None of the above...
        else:
            self._ui.print(repr(type(value)))
Beispiel #15
0
 def step_coro(*args, **kwargs):
     try:
         if getcoroutinestate(coro) != CORO_CLOSED:
             coro.send((
                 args,
                 kwargs,
             ))(step_coro)
     except StopIteration:
         pass
Beispiel #16
0
 def _step_coro(self, *args, **kwargs):
     coro = self._root_coro
     try:
         if getcoroutinestate(coro) != CORO_CLOSED:
             coro.send((args, kwargs, ))(self._step_coro)
     except StopIteration:
         pass
     else:
         if self._cancel_called and self._is_cancellable:
             self._actual_cancel()
Beispiel #17
0
    async def test_spawn_after_shutdown(self):
        async def activity(value):
            return value

        async with Scope() as scope:
            pass
        payload = activity(3)
        with pytest.raises(RuntimeError):
            scope.do(payload)
        assert inspect.getcoroutinestate(payload) == inspect.CORO_CLOSED
async def test_normal_exit(nursery, ed):
    from inspect import getcoroutinestate, CORO_SUSPENDED, CORO_CLOSED
    import trio
    import asynckivy as ak
    from asynckivy.compatibility.trio import run_coro_under_trio
    
    async def ak_func():
        await ak.event(ed, 'on_test')
    ak_coro = ak_func()
    async def trio_func(*, task_status=trio.TASK_STATUS_IGNORED):
        await run_coro_under_trio(ak_coro, task_status=task_status)
        nonlocal done; done = True

    done = False
    await nursery.start(trio_func)
    assert getcoroutinestate(ak_coro) == CORO_SUSPENDED
    assert not done
    ed.dispatch('on_test')
    assert getcoroutinestate(ak_coro) == CORO_CLOSED
    await trio.sleep(.01)
    assert done
Beispiel #19
0
 async def _do_it(self, start_fn, *args):
     # On CPython 3.5.2 (but not 3.5.0), coroutines get cranky if you try
     # to iterate them after they're exhausted. Generators OTOH just raise
     # StopIteration. We want to convert the one into the other, so we need
     # to avoid iterating stopped coroutines.
     if getcoroutinestate(self._coroutine) is CORO_CLOSED:
         raise StopAsyncIteration()
     if self.ag_running:
         raise ValueError("async generator already executing")
     try:
         self.ag_running = True
         return await ANextIter(self._it, start_fn, *args)
     finally:
         self.ag_running = False
Beispiel #20
0
 async def _do_it(self, start_fn, *args):
     # On CPython 3.5.2 (but not 3.5.0), coroutines get cranky if you try
     # to iterate them after they're exhausted. Generators OTOH just raise
     # StopIteration. We want to convert the one into the other, so we need
     # to avoid iterating stopped coroutines.
     if getcoroutinestate(self._coroutine) is CORO_CLOSED:
         raise StopAsyncIteration()
     if self.ag_running:
         raise ValueError("async generator already executing")
     try:
         self.ag_running = True
         return await ANextIter(self._it, start_fn, *args)
     finally:
         self.ag_running = False
Beispiel #21
0
 def __repr__(self):
     cstate = ''
     if inspect.iscoroutine(self.coro):
         crst = inspect.getcoroutinestate(self.coro)
         cstate = f' {crst} {self._coro_info(self.coro)}'
     tstate = self.state
     if self.is_done():
         result = self.result(False)
         if result is not None:
             tstate += f'={result!r}'
     return (f'<{self.__class__.__name__}'
             f' {tstate}'
             f' #{self.task_id}@{self.creation}{cstate}'
             '>')
Beispiel #22
0
 async def aclose(self):
     state = getcoroutinestate(self._coroutine)
     if state is CORO_CREATED:
         # Make sure that aclose() on an unstarted generator returns
         # successfully and prevents future iteration.
         self._it.close()
         return
     elif state is CORO_CLOSED:
         return
     try:
         await self.athrow(GeneratorExit)
     except (GeneratorExit, StopAsyncIteration):
         pass
     else:
         raise RuntimeError("async_generator ignored GeneratorExit")
Beispiel #23
0
 async def aclose(self):
     state = getcoroutinestate(self._coroutine)
     if state is CORO_CREATED:
         # Make sure that aclose() on an unstarted generator returns
         # successfully and prevents future iteration.
         self._it.close()
         return
     elif state is CORO_CLOSED:
         return
     try:
         await self.athrow(GeneratorExit)
     except (GeneratorExit, StopAsyncIteration):
         pass
     else:
         raise RuntimeError("async_generator ignored GeneratorExit")
Beispiel #24
0
    async def _delayed_coro(self, delay: t.Union[float, int], coro: t.Awaitable, task_id):
        try:
            #await the timed delay
            log.debug(f'Waiting {delay} seconds before executing coroutine with Id: {task_id}')
            await asyncio.sleep(delay)

            #time is up, execute the callback
            log.debug(f'Delay complete for coroutine {task_id}; executing coroutine')
            await asyncio.shield(coro)
        #use a finally so that the coro is closed even if it throws
        finally:
            if inspect.getcoroutinestate(coro) == 'CORO_CREATED':
                log.debug(f'Explicitly closing the coroutine for #{task_id}.')
                coro.close()
            else:
                log.debug(f'Finally block reached for #{task_id}')
Beispiel #25
0
    def _coro_info(klass, coro):
        if not inspect.iscoroutine(coro):
            return f'{coro!r}'

        result = []
        result.append(coro.__name__)
        c = coro
        while True:
            if inspect.getcoroutinestate(c) == 'CORO_SUSPENDED':
                if inspect.isgenerator(c.cr_await):
                    result.append(
                        f'{c.cr_await.__name__}@{klass._frame(c.cr_frame)}')
                else:
                    c = c.cr_await
                    continue
            break
        return ' '.join(x for x in result if x)
Beispiel #26
0
    async def _postpone(self, task_name: t.Hashable, coro: t.Coroutine,
                        delay: t.Union[int, float]) -> None:
        """
        Execute `coro` after `delay` amount of seconds.

        In case the `coro` would get cancelled during the sleep, close it to
        prevent never awaited error.
        """
        try:
            await asyncio.sleep(delay)
            # Prevent `coro` from canceling itself by shielding it
            await asyncio.shield(coro)
        finally:
            # Prevent coroutine never awaited error if it got cancelled during sleep
            # But only do so if it wasn't awaited yet, since the coro can also cancel itself
            if inspect.getcoroutinestate(coro) == "CORO_CREATED":
                logger.debug(
                    f"Aborting the coroutine from {self.id}:{task_name} task.")
                coro.close()
Beispiel #27
0
 async def aclose(self):
     state = getcoroutinestate(self._coroutine)
     if state is CORO_CLOSED or self._closed:
         return
     # Make sure that even if we raise "async_generator ignored
     # GeneratorExit", and thus fail to exhaust the coroutine,
     # __del__ doesn't complain again.
     self._closed = True
     if state is CORO_CREATED:
         # Make sure that aclose() on an unstarted generator returns
         # successfully and prevents future iteration.
         self._it.close()
         return
     try:
         await self.athrow(GeneratorExit)
     except (GeneratorExit, StopAsyncIteration):
         self._pypy_issue2786_workaround.discard(self._coroutine)
     else:
         raise RuntimeError("async_generator ignored GeneratorExit")
async def child_a(ctx):
    from inspect import getcoroutinestate, CORO_RUNNING
    import asyncgui as ag
    await ctx['e_begin'].wait()
    await ctx['e'].wait()
    assert getcoroutinestate(ctx['task_b'].root_coro) == CORO_RUNNING
    ctx['main_task'].cancel()
    what = ctx['what_a_should_do']
    if what == 'nothing':
        return
    elif what == 'suspend':
        await ag.sleep_forever()
    elif what == 'fail':
        raise ZeroDivisionError
    elif what == 'cancel_self':
        task_a = await ag.get_current_task()
        task_a.cancel()
        await ag.sleep_forever()
    else:
        pytest.fail(f"Invalid value: {what}")
Beispiel #29
0
    def schedule(self, task_id: t.Hashable, coroutine: t.Coroutine) -> None:
        """
        Schedule the execution of a `coroutine`.

        If a task with `task_id` already exists, close `coroutine` instead of scheduling it. This
        prevents unawaited coroutine warnings. Don't pass a coroutine that'll be re-used elsewhere.
        """
        self._log.trace(f"Scheduling task #{task_id}...")

        msg = f"Cannot schedule an already started coroutine for #{task_id}"
        assert inspect.getcoroutinestate(coroutine) == "CORO_CREATED", msg

        if task_id in self._scheduled_tasks:
            self._log.debug(f"Did not schedule task #{task_id}; task was already scheduled.")
            coroutine.close()
            return

        task = asyncio.create_task(coroutine, name=f"{self.name}_{task_id}")
        task.add_done_callback(partial(self._task_done_callback, task_id))

        self._scheduled_tasks[task_id] = task
        self._log.debug(f"Scheduled task #{task_id} {id(task)}.")
Beispiel #30
0
def start(coro_or_task: Coro_or_Task) -> Coro_or_Task:
    '''Starts a asynckivy-flavored coroutine or a Task. It is recommended to
    pass a Task instead of a coroutine if a coroutine is going to live long
    time, as Task is flexibler and has information that may be useful for
    debugging.

    Returns the argument itself.
    '''
    def step_coro(*args, **kwargs):
        try:
            if getcoroutinestate(coro) != CORO_CLOSED:
                coro.send((
                    args,
                    kwargs,
                ))(step_coro)
        except StopIteration:
            pass

    if isinstance(coro_or_task, Task):
        task = coro_or_task
        if task._state is not TaskState.CREATED:
            raise ValueError(f"{task} was already started")
        step_coro._task = task
        coro = task.root_coro
    else:
        coro = coro_or_task
        if getcoroutinestate(coro) != CORO_CREATED:
            raise ValueError("Coroutine was already started")
        step_coro._task = None

    try:
        coro.send(None)(step_coro)
    except StopIteration:
        pass

    return coro_or_task
Beispiel #31
0
 def safe_send(gen, data):
     state = inspect.getcoroutinestate(gen)
     if state not in {"CORO_RUNNING", "CORO_CLOSED"}:
         return send(gen, data)
Beispiel #32
0
 def _is_cancellable(self) -> bool:
     '''Whether the task can immediately be cancelled.'''
     return (not self._cancel_protection) and \
         getcoroutinestate(self._root_coro) != CORO_RUNNING
Beispiel #33
0
 def a():
     self.assertEqual(inspect.getcoroutinestate(coro_b), inspect.CORO_RUNNING)
     self.assertIsNone(coro_b.cr_await)
     yield
     self.assertEqual(inspect.getcoroutinestate(coro_b), inspect.CORO_RUNNING)
     self.assertIsNone(coro_b.cr_await)
Beispiel #34
0
def main():
    ##########################################################################
    # native coroutine example
    ##########################################################################
    print(str.format(
        'coroutine_function function is coroutine function => {}',
        inspect.iscoroutinefunction(coroutine_function)
    ))

    print(str.format(
        'coroutine_function type => {}', type(coroutine_function)
    ))

    # create native python coroutine
    coroutine = coroutine_function()

    print(str.format(
        'coroutine is coroutine => {}', inspect.iscoroutine(coroutine)
    ))

    print(str.format('coroutine type => {}', type(coroutine)))

    print(str.format(
        'coroutine state => {}', inspect.getcoroutinestate(coroutine)
    ))

    print(str.format(
        'coroutine locals => {}', inspect.getcoroutinelocals(coroutine)
    ))

    ##########################################################################
    # generator example
    ##########################################################################
    print(str.format(
        'generator_function function is generator function => {}',
        inspect.isgeneratorfunction(generator_function)
    ))

    print(str.format(
        'generator_function type => {}', type(generator_function)
    ))

    # create generator object
    generator = generator_function()

    print(str.format(
        'generator is generator object => {}', inspect.isgenerator(generator)
    ))

    print(str.format('generator object type => {}', type(generator)))

    print(str.format(
        'generator object state => {}', inspect.getgeneratorstate(generator)
    ))

    # iterate generator
    generator_result1 = next(generator)
    print(str.format(
        'generator object last iterate result => {}', generator_result1
    ))

    print(str.format(
        'generator object state => {}', inspect.getgeneratorstate(generator)
    ))

    try:
        generator_result2 = next(generator)
        print(str.format(
            'generator object last iterate result => {}', generator_result2
        ))
    except StopIteration:
        print(str.format('[#] generator object iterated'))

    print(str.format(
        'generator object state => {}', inspect.getgeneratorstate(generator)
    ))

    ##########################################################################
    # ordinary function example
    ##########################################################################
    print(str.format(
        'ordinary_function() is ordinary function: {}',
        inspect.isfunction(ordinary_function)
    ))