Test Setup Failed
Pull Request — master (#421)
by Daniel
03:37
created

pynvim.attach()   B

Complexity

Conditions 7

Size

Total Lines 43
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 7.1429

Importance

Changes 0
Metric Value
cc 7
eloc 12
nop 6
dl 0
loc 43
rs 8
c 0
b 0
f 0
ccs 6
cts 7
cp 0.8571
crap 7.1429
1
"""Python client for Nvim.
2
3
Client library for talking with Nvim processes via its msgpack-rpc API.
4
"""
5 6
import logging
6 6
import os
7 6
import sys
8
9 6
from .api import Nvim, NvimError
10 6
from .compat import IS_PYTHON3
11 6
from .msgpack_rpc import (ErrorResponse, child_session, socket_session,
12
                          stdio_session, tcp_session)
13 6
from .plugin import (Host, autocmd, command, decode, encoding, function,
14
                     plugin, rpc_export, shutdown_hook)
15 6
from .util import VERSION, Version
16
17
18 6
__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',
22
           'ErrorResponse')
23
24
25 6
def start_host(session=None):
26
    """Promote the current process into python plugin host for Nvim.
27
28
    Start msgpack-rpc event loop for `session`, listening for Nvim requests
29
    and notifications. It registers Nvim commands for loading/unloading
30
    python plugins.
31
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.
35
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.
39
    """
40
    plugins = []
41
    for arg in sys.argv:
42
        _, ext = os.path.splitext(arg)
43
        if ext == '.py':
44
            plugins.append(arg)
45
        elif os.path.isdir(arg):
46
            init = os.path.join(arg, '__init__.py')
47
            if os.path.isfile(init):
48
                plugins.append(arg)
49
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):
54
        dup = path + ".py"
55
        if os.path.isdir(path) and dup in plugins:
56
            plugins.remove(dup)
57
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"]:
61
        name = "script"
62
    else:
63
        name = "rplugin"
64
65
    setup_logging(name)
66
67
    if not session:
68
        session = stdio_session()
69
    nvim = Nvim.from_session(session)
70
71
    if nvim.version.api_level < 1:
72
        sys.stderr.write("This version of pynvim "
73
                         "requires nvim 0.1.6 or later")
74
        sys.exit(1)
75
76
    host = Host(nvim)
77
    host.start(plugins)
78
79
80 6
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.
83
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.
86
    Thus, instead of:
87
        from pynvim import socket_session, Nvim
88
        session = tcp_session(address=<address>, port=<port>)
89
        nvim = Nvim.from_session(session)
90
    You can now do:
91
        from pynvim import attach
92
        nvim = attach('tcp', address=<address>, port=<port>)
93
    And also:
94
        nvim = attach('socket', path=<path>)
95
        nvim = attach('child', argv=<argv>)
96
        nvim = attach('stdio')
97
98
    When the session is not needed anymore, it is recommended to explicitly
99
    close it:
100
       nvim.close()
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.
107
108
109
    """
110 6
    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
114
               None)
115
116 6
    if not session:
117
        raise Exception('Unknown session type "%s"' % session_type)
118
119 6
    if decode is None:
120 6
        decode = IS_PYTHON3
121
122 6
    return Nvim.from_session(session).with_decode(decode)
123
124
125 6
def setup_logging(name):
126
    """Setup logging according to environment variables."""
127 6
    logger = logging.getLogger(__name__)
128 6
    if 'NVIM_PYTHON_LOG_FILE' in os.environ:
129 6
        prefix = os.environ['NVIM_PYTHON_LOG_FILE'].strip()
130 6
        major_version = sys.version_info[0]
131 6
        logfile = '{}_py{}_{}'.format(prefix, major_version, name)
132 6
        handler = logging.FileHandler(logfile, 'w', 'utf-8')
133 6
        handler.formatter = logging.Formatter(
134
            '%(asctime)s [%(levelname)s @ '
135
            '%(filename)s:%(funcName)s:%(lineno)s] %(process)s - %(message)s')
136 6
        logging.root.addHandler(handler)
137 6
        level = logging.INFO
138 6
        env_log_level = os.environ.get('NVIM_PYTHON_LOG_LEVEL', None)
139 6
        if env_log_level is not None:
140 6
            lvl = getattr(logging, env_log_level.strip(), None)
141 6
            if isinstance(lvl, int):
142
                level = lvl
143
            else:
144 6
                logger.warning('Invalid NVIM_PYTHON_LOG_LEVEL: %r, using INFO.',
145
                               env_log_level)
146 6
        logger.setLevel(level)
147
148
149
# Required for python 2.6
150 6
class NullHandler(logging.Handler):
151 6
    def emit(self, record):
152 6
        pass
153
154
155 6
if not logging.root.handlers:
156
    logging.root.addHandler(NullHandler())
157