1 """Python client for Nvim.
3 Client library for talking with Nvim processes via its msgpack-rpc API.
9 from pynvim.api import Nvim, NvimError
10 from pynvim.compat import IS_PYTHON3
11 from pynvim.msgpack_rpc import (ErrorResponse, child_session, socket_session,
12 stdio_session, tcp_session)
13 from pynvim.plugin import (Host, autocmd, command, decode, encoding, function,
14 plugin, rpc_export, shutdown_hook)
15 from pynvim.util import VERSION, Version
18 __all__ = ('tcp_session', 'socket_session', 'stdio_session', 'child_session',
19 'start_host', 'autocmd', 'command', 'encoding', 'decode',
20 'function', 'plugin', 'rpc_export', 'Host', 'Nvim', 'NvimError',
21 'Version', 'VERSION', 'shutdown_hook', 'attach', 'setup_logging',
25 def start_host(session=None):
26 """Promote the current process into python plugin host for Nvim.
28 Start msgpack-rpc event loop for `session`, listening for Nvim requests
29 and notifications. It registers Nvim commands for loading/unloading
32 The sys.stdout and sys.stderr streams are redirected to Nvim through
33 `session`. That means print statements probably won't work as expected
34 while this function doesn't return.
36 This function is normally called at program startup and could have been
37 defined as a separate executable. It is exposed as a library function for
38 testing purposes only.
42 _, ext = os.path.splitext(arg)
45 elif os.path.isdir(arg):
46 init = os.path.join(arg, '__init__.py')
47 if os.path.isfile(init):
50 # This is a special case to support the old workaround of
51 # adding an empty .py file to make a package directory
52 # visible, and it should be removed soon.
53 for path in list(plugins):
55 if os.path.isdir(path) and dup in plugins:
58 # Special case: the legacy scripthost receives a single relative filename
59 # while the rplugin host will receive absolute paths.
60 if plugins == ["script_host.py"]:
68 session = stdio_session()
69 nvim = Nvim.from_session(session)
71 if nvim.version.api_level < 1:
72 sys.stderr.write("This version of pynvim "
73 "requires nvim 0.1.6 or later")
80 def attach(session_type, address=None, port=None,
81 path=None, argv=None, decode=None):
82 """Provide a nicer interface to create python api sessions.
84 Previous machinery to create python api sessions is still there. This only
85 creates a facade function to make things easier for the most usual cases.
87 from pynvim import socket_session, Nvim
88 session = tcp_session(address=<address>, port=<port>)
89 nvim = Nvim.from_session(session)
91 from pynvim import attach
92 nvim = attach('tcp', address=<address>, port=<port>)
94 nvim = attach('socket', path=<path>)
95 nvim = attach('child', argv=<argv>)
96 nvim = attach('stdio')
98 When the session is not needed anymore, it is recommended to explicitly
101 It is also possible to use the session as a context mangager:
102 with attach('socket', path=thepath) as nvim:
103 print(nvim.funcs.getpid())
104 print(nvim.current.line)
105 This will automatically close the session when you're done with it, or
106 when an error occured.
110 session = (tcp_session(address, port) if session_type == 'tcp' else
111 socket_session(path) if session_type == 'socket' else
112 stdio_session() if session_type == 'stdio' else
113 child_session(argv) if session_type == 'child' else
117 raise Exception('Unknown session type "%s"' % session_type)
122 return Nvim.from_session(session).with_decode(decode)
125 def setup_logging(name):
126 """Setup logging according to environment variables."""
127 logger = logging.getLogger(__name__)
128 if 'NVIM_PYTHON_LOG_FILE' in os.environ:
129 prefix = os.environ['NVIM_PYTHON_LOG_FILE'].strip()
130 major_version = sys.version_info[0]
131 logfile = '{}_py{}_{}'.format(prefix, major_version, name)
132 handler = logging.FileHandler(logfile, 'w', 'utf-8')
133 handler.formatter = logging.Formatter(
134 '%(asctime)s [%(levelname)s @ '
135 '%(filename)s:%(funcName)s:%(lineno)s] %(process)s - %(message)s')
136 logging.root.addHandler(handler)
138 env_log_level = os.environ.get('NVIM_PYTHON_LOG_LEVEL', None)
139 if env_log_level is not None:
140 lvl = getattr(logging, env_log_level.strip(), None)
141 if isinstance(lvl, int):
144 logger.warning('Invalid NVIM_PYTHON_LOG_LEVEL: %r, using INFO.',
146 logger.setLevel(level)
149 # Required for python 2.6
150 class NullHandler(logging.Handler):
151 def emit(self, record):
155 if not logging.root.handlers:
156 logging.root.addHandler(NullHandler())