1from __future__ import unicode_literals
2
3import threading
4
5from prompt_toolkit.utils import is_windows
6
7from .base import EventLoop
8
9__all__ = [
10    'create_event_loop',
11    'create_asyncio_event_loop',
12    'use_asyncio_event_loop',
13    'get_event_loop',
14    'set_event_loop',
15    'run_in_executor',
16    'call_from_executor',
17    'run_until_complete',
18]
19
20
21def create_event_loop(recognize_win32_paste=True):
22    """
23    Create and return an
24    :class:`~prompt_toolkit.eventloop.base.EventLoop` instance.
25    """
26    if is_windows():
27        from .win32 import Win32EventLoop
28        return Win32EventLoop(recognize_paste=recognize_win32_paste)
29    else:
30        from .posix import PosixEventLoop
31        return PosixEventLoop()
32
33
34def _get_asyncio_loop_cls():
35    # Inline import, to make sure the rest doesn't break on Python 2. (Where
36    # asyncio is not available.)
37    if is_windows():
38        from prompt_toolkit.eventloop.asyncio_win32 import Win32AsyncioEventLoop as AsyncioEventLoop
39    else:
40        from prompt_toolkit.eventloop.asyncio_posix import PosixAsyncioEventLoop as AsyncioEventLoop
41    return AsyncioEventLoop
42
43
44def create_asyncio_event_loop(loop=None):
45    """
46    Returns an asyncio :class:`~prompt_toolkit.eventloop.EventLoop` instance
47    for usage in a :class:`~prompt_toolkit.application.Application`. It is a
48    wrapper around an asyncio loop.
49
50    :param loop: The asyncio eventloop (or `None` if the default asyncioloop
51                 should be used.)
52    """
53    AsyncioEventLoop = _get_asyncio_loop_cls()
54    return AsyncioEventLoop(loop)
55
56
57def use_asyncio_event_loop(loop=None):
58    """
59    Use the asyncio event loop for prompt_toolkit applications.
60    """
61    # Don't create a new loop if the current one uses asyncio already.
62    current_loop = get_event_loop()
63    if current_loop and isinstance(current_loop, _get_asyncio_loop_cls()):
64        return
65
66    set_event_loop(create_asyncio_event_loop(loop))
67
68
69_loop = None
70_loop_lock = threading.RLock()
71
72
73def get_event_loop():
74    """
75    Return the current event loop.
76    This will create a new loop if no loop was set yet.
77    """
78    # When this function is called for the first time, and no loop has been
79    # set: create one.
80    global _loop
81
82    with _loop_lock:
83        # The following two lines are not atomic. I ended up in a situation
84        # where two threads were calling `get_event_loop()` at the same time,
85        # and that way we had two event loop instances. On one of the
86        # instances, `call_from_executor` was called, but never executed
87        # because that loop was not running.
88        if _loop is None:
89            _loop = create_event_loop()
90
91        return _loop
92
93
94def set_event_loop(loop):
95    """
96    Set the current event loop.
97
98    :param loop: `EventLoop` instance or None. (Pass `None` to clear the
99        current loop.)
100    """
101    assert loop is None or isinstance(loop, EventLoop)
102    global _loop
103    _loop = loop
104
105
106def run_in_executor(callback, _daemon=False):
107    """
108    Run a long running function in a background thread.
109    """
110    return get_event_loop().run_in_executor(callback, _daemon=_daemon)
111
112
113def call_from_executor(callback, _max_postpone_until=None):
114    """
115    Call this function in the main event loop.
116    """
117    return get_event_loop().call_from_executor(
118        callback, _max_postpone_until=_max_postpone_until)
119
120
121def run_until_complete(future, inputhook=None):
122    """
123    Keep running until this future has been set.
124    Return the Future's result, or raise its exception.
125    """
126    return get_event_loop().run_until_complete(future, inputhook=inputhook)
127