Completed
Push — master ( 4280e4...9ac47f )
by Björn
9s
created

attach()   C

Complexity

Conditions 7

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 7.1429

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
dl 0
loc 31
ccs 6
cts 7
cp 0.8571
crap 7.1429
rs 5.5
c 1
b 0
f 0
1
"""Python client for Nvim.
2
3
Client library for talking with Nvim processes via it's msgpack-rpc API.
4
"""
5 6
import logging
6 6
import os
7 6
import sys
8
9 6
from .api import Nvim
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
16
17 6
__all__ = ('tcp_session', 'socket_session', 'stdio_session', 'child_session',
18
           'start_host', 'autocmd', 'command', 'encoding', 'decode',
19
           'function', 'plugin', 'rpc_export', 'Host', 'Nvim',
20
           'shutdown_hook', 'attach', 'setup_logging', 'ErrorResponse')
21
22
23 6
def start_host(session=None):
24
    """Promote the current process into python plugin host for Nvim.
25
26
    Start msgpack-rpc event loop for `session`, listening for Nvim requests
27
    and notifications. It registers Nvim commands for loading/unloading
28
    python plugins.
29
30
    The sys.stdout and sys.stderr streams are redirected to Nvim through
31
    `session`. That means print statements probably won't work as expected
32
    while this function doesn't return.
33
34
    This function is normally called at program startup and could have been
35
    defined as a separate executable. It is exposed as a library function for
36
    testing purposes only.
37
    """
38
    plugins = []
39
    for arg in sys.argv:
40
        _, ext = os.path.splitext(arg)
41
        if ext == '.py':
42
            plugins.append(arg)
43
        elif os.path.isdir(arg):
44
            init = os.path.join(arg, '__init__.py')
45
            if os.path.isfile(init):
46
                plugins.append(arg)
47
48
    # This is a special case to support the old workaround of
49
    # adding an empty .py file to make a package directory
50
    # visible, and it should be removed soon.
51
    for path in list(plugins):
52
        dup = path + ".py"
53
        if os.path.isdir(path) and dup in plugins:
54
            plugins.remove(dup)
55
56
    setup_logging()
57
58
    if not session:
59
        session = stdio_session()
60
    host = Host(Nvim.from_session(session))
61
    host.start(plugins)
62
63
64 6
def attach(session_type, address=None, port=None,
65
           path=None, argv=None, decode=None):
66
    """Provide a nicer interface to create python api sessions.
67
68
    Previous machinery to create python api sessions is still there. This only
69
    creates a facade function to make things easier for the most usual cases.
70
    Thus, instead of:
71
        from neovim import socket_session, Nvim
72
        session = tcp_session(address=<address>, port=<port>)
73
        nvim = Nvim.from_session(session)
74
    You can now do:
75
        from neovim import attach
76
        nvim = attach('tcp', address=<address>, port=<port>)
77
    And also:
78
        nvim = attach('socket', path=<path>)
79
        nvim = attach('child', argv=<argv>)
80
        nvim = attach('stdio')
81
    """
82 6
    session = (tcp_session(address, port) if session_type == 'tcp' else
83
               socket_session(path) if session_type == 'socket' else
84
               stdio_session() if session_type == 'stdio' else
85
               child_session(argv) if session_type == 'child' else
86
               None)
87
88 6
    if not session:
89
        raise Exception('Unknown session type "%s"' % session_type)
90
91 6
    if decode is None:
92 6
        decode = IS_PYTHON3
93
94 6
    return Nvim.from_session(session).with_decode(decode)
95
96
97 6
def setup_logging():
98
    """Setup logging according to environment variables."""
99 6
    logger = logging.getLogger(__name__)
100 6
    if 'NVIM_PYTHON_LOG_FILE' in os.environ:
101
        logfile = (os.environ['NVIM_PYTHON_LOG_FILE'].strip() +
102
                   '_' + str(os.getpid()))
103
        handler = logging.FileHandler(logfile, 'w')
104
        handler.formatter = logging.Formatter(
105
            '%(asctime)s [%(levelname)s @ '
106
            '%(filename)s:%(funcName)s:%(lineno)s] %(process)s - %(message)s')
107
        logging.root.addHandler(handler)
108
        level = logging.INFO
109
        if 'NVIM_PYTHON_LOG_LEVEL' in os.environ:
110
            l = getattr(logging,
111
                        os.environ['NVIM_PYTHON_LOG_LEVEL'].strip(),
112
                        level)
113
            if isinstance(l, int):
114
                level = l
115
        logger.setLevel(level)
116
117
118
# Required for python 2.6
119 6
class NullHandler(logging.Handler):
120 6
    def emit(self, record):
121 6
        pass
122
123
124 6
if not logging.root.handlers:
125
    logging.root.addHandler(NullHandler())
126