efficient vim config
[dotfiles/.git] / .local / lib / python2.7 / site-packages / pynvim / msgpack_rpc / event_loop / base.py
1 """Common code for event loop implementations."""
2 import logging
3 import signal
4 import threading
5
6
7 logger = logging.getLogger(__name__)
8 debug, info, warn = (logger.debug, logger.info, logger.warning,)
9
10
11 # When signals are restored, the event loop library may reset SIGINT to SIG_DFL
12 # which exits the program. To be able to restore the python interpreter to it's
13 # default state, we keep a reference to the default handler
14 default_int_handler = signal.getsignal(signal.SIGINT)
15 main_thread = threading.current_thread()
16
17
18 class BaseEventLoop(object):
19
20     """Abstract base class for all event loops.
21
22     Event loops act as the bottom layer for Nvim sessions created by this
23     library. They hide system/transport details behind a simple interface for
24     reading/writing bytes to the connected Nvim instance.
25
26     This class exposes public methods for interacting with the underlying
27     event loop and delegates implementation-specific work to the following
28     methods, which subclasses are expected to implement:
29
30     - `_init()`: Implementation-specific initialization
31     - `_connect_tcp(address, port)`: connect to Nvim using tcp/ip
32     - `_connect_socket(path)`: Same as tcp, but use a UNIX domain socket or
33       named pipe.
34     - `_connect_stdio()`: Use stdin/stdout as the connection to Nvim
35     - `_connect_child(argv)`: Use the argument vector `argv` to spawn an
36       embedded Nvim that has its stdin/stdout connected to the event loop.
37     - `_start_reading()`: Called after any of _connect_* methods. Can be used
38       to perform any post-connection setup or validation.
39     - `_send(data)`: Send `data`(byte array) to Nvim. The data is only
40     - `_run()`: Runs the event loop until stopped or the connection is closed.
41       calling the following methods when some event happens:
42       actually sent when the event loop is running.
43       - `_on_data(data)`: When Nvim sends some data.
44       - `_on_signal(signum)`: When a signal is received.
45       - `_on_error(message)`: When a non-recoverable error occurs(eg:
46         connection lost)
47     - `_stop()`: Stop the event loop
48     - `_interrupt(data)`: Like `stop()`, but may be called from other threads
49       this.
50     - `_setup_signals(signals)`: Add implementation-specific listeners for
51       for `signals`, which is a list of OS-specific signal numbers.
52     - `_teardown_signals()`: Removes signal listeners set by `_setup_signals`
53     """
54
55     def __init__(self, transport_type, *args):
56         """Initialize and connect the event loop instance.
57
58         The only arguments are the transport type and transport-specific
59         configuration, like this:
60
61         >>> BaseEventLoop('tcp', '127.0.0.1', 7450)
62         Traceback (most recent call last):
63             ...
64         AttributeError: 'BaseEventLoop' object has no attribute '_init'
65         >>> BaseEventLoop('socket', '/tmp/nvim-socket')
66         Traceback (most recent call last):
67             ...
68         AttributeError: 'BaseEventLoop' object has no attribute '_init'
69         >>> BaseEventLoop('stdio')
70         Traceback (most recent call last):
71             ...
72         AttributeError: 'BaseEventLoop' object has no attribute '_init'
73         >>> BaseEventLoop('child',
74                 ['nvim', '--embed', '--headless', '-u', 'NONE'])
75         Traceback (most recent call last):
76             ...
77         AttributeError: 'BaseEventLoop' object has no attribute '_init'
78
79         This calls the implementation-specific initialization
80         `_init`, one of the `_connect_*` methods(based on `transport_type`)
81         and `_start_reading()`
82         """
83         self._transport_type = transport_type
84         self._signames = dict((k, v) for v, k in signal.__dict__.items()
85                               if v.startswith('SIG'))
86         self._on_data = None
87         self._error = None
88         self._init()
89         try:
90             getattr(self, '_connect_{}'.format(transport_type))(*args)
91         except Exception as e:
92             self.close()
93             raise e
94         self._start_reading()
95
96     def connect_tcp(self, address, port):
97         """Connect to tcp/ip `address`:`port`. Delegated to `_connect_tcp`."""
98         pass # replaces next logging statement
99         #info('Connecting to TCP address: %s:%d', address, port)
100         self._connect_tcp(address, port)
101
102     def connect_socket(self, path):
103         """Connect to socket at `path`. Delegated to `_connect_socket`."""
104         pass # replaces next logging statement
105         #info('Connecting to %s', path)
106         self._connect_socket(path)
107
108     def connect_stdio(self):
109         """Connect using stdin/stdout. Delegated to `_connect_stdio`."""
110         pass # replaces next logging statement
111         #info('Preparing stdin/stdout for streaming data')
112         self._connect_stdio()
113
114     def connect_child(self, argv):
115         """Connect a new Nvim instance. Delegated to `_connect_child`."""
116         pass # replaces next logging statement
117         #info('Spawning a new nvim instance')
118         self._connect_child(argv)
119
120     def send(self, data):
121         """Queue `data` for sending to Nvim."""
122         pass # replaces next logging statement
123         #debug("Sending '%s'", data)
124         self._send(data)
125
126     def threadsafe_call(self, fn):
127         """Call a function in the event loop thread.
128
129         This is the only safe way to interact with a session from other
130         threads.
131         """
132         self._threadsafe_call(fn)
133
134     def run(self, data_cb):
135         """Run the event loop."""
136         if self._error:
137             err = self._error
138             if isinstance(self._error, KeyboardInterrupt):
139                 # KeyboardInterrupt is not destructive(it may be used in
140                 # the REPL).
141                 # After throwing KeyboardInterrupt, cleanup the _error field
142                 # so the loop may be started again
143                 self._error = None
144             raise err
145         self._on_data = data_cb
146         if threading.current_thread() == main_thread:
147             self._setup_signals([signal.SIGINT, signal.SIGTERM])
148         pass # replaces next logging statement
149         #debug('Entering event loop')
150         self._run()
151         pass # replaces next logging statement
152         #debug('Exited event loop')
153         if threading.current_thread() == main_thread:
154             self._teardown_signals()
155             signal.signal(signal.SIGINT, default_int_handler)
156         self._on_data = None
157
158     def stop(self):
159         """Stop the event loop."""
160         self._stop()
161         pass # replaces next logging statement
162         #debug('Stopped event loop')
163
164     def close(self):
165         """Stop the event loop."""
166         self._close()
167         pass # replaces next logging statement
168         #debug('Closed event loop')
169
170     def _on_signal(self, signum):
171         msg = 'Received {}'.format(self._signames[signum])
172         pass # replaces next logging statement
173         #debug(msg)
174         if signum == signal.SIGINT and self._transport_type == 'stdio':
175             # When the transport is stdio, we are probably running as a Nvim
176             # child process. In that case, we don't want to be killed by
177             # ctrl+C
178             return
179         cls = Exception
180         if signum == signal.SIGINT:
181             cls = KeyboardInterrupt
182         self._error = cls(msg)
183         self.stop()
184
185     def _on_error(self, error):
186         pass # replaces next logging statement
187         #debug(error)
188         self._error = OSError(error)
189         self.stop()
190
191     def _on_interrupt(self):
192         self.stop()