File: __init__.py

package info (click to toggle)
python-pynvim 0.5.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 432 kB
  • sloc: python: 3,040; makefile: 4
file content (175 lines) | stat: -rw-r--r-- 6,126 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
"""Python client for Nvim.

Client library for talking with Nvim processes via its msgpack-rpc API.
"""
import logging
import os
import sys
from types import SimpleNamespace as Version
from typing import List, Optional, cast, overload

from pynvim._version import VERSION, __version__
from pynvim.api import Nvim, NvimError
from pynvim.msgpack_rpc import (ErrorResponse, Session, TTransportType,
                                child_session, socket_session, stdio_session,
                                tcp_session)
from pynvim.plugin import (Host, autocmd, command, decode, encoding, function,
                           plugin, rpc_export, shutdown_hook)

if sys.version_info < (3, 8):
    from typing_extensions import Literal
else:
    from typing import Literal


__all__ = ('tcp_session', 'socket_session', 'stdio_session', 'child_session',
           'start_host', 'autocmd', 'command', 'encoding', 'decode',
           'function', 'plugin', 'rpc_export', 'Host', 'Nvim', 'NvimError',
           'Version', 'VERSION', '__version__',
           'shutdown_hook', 'attach', 'setup_logging', 'ErrorResponse',
           )


def start_host(session: Optional[Session] = None) -> None:
    """Promote the current process into python plugin host for Nvim.

    Start msgpack-rpc event loop for `session`, listening for Nvim requests
    and notifications. It registers Nvim commands for loading/unloading
    python plugins.

    The sys.stdout and sys.stderr streams are redirected to Nvim through
    `session`. That means print statements probably won't work as expected
    while this function doesn't return.

    This function is normally called at program startup and could have been
    defined as a separate executable. It is exposed as a library function for
    testing purposes only.
    """
    plugins = []
    for arg in sys.argv:
        _, ext = os.path.splitext(arg)
        if ext == '.py':
            plugins.append(arg)
        elif os.path.isdir(arg):
            init = os.path.join(arg, '__init__.py')
            if os.path.isfile(init):
                plugins.append(arg)

    # This is a special case to support the old workaround of
    # adding an empty .py file to make a package directory
    # visible, and it should be removed soon.
    for path in list(plugins):
        dup = path + ".py"
        if os.path.isdir(path) and dup in plugins:
            plugins.remove(dup)

    # Special case: the legacy scripthost receives a single relative filename
    # while the rplugin host will receive absolute paths.
    if plugins == ["script_host.py"]:
        name = "script"
    else:
        name = "rplugin"

    setup_logging(name)

    if not session:
        session = stdio_session()
    nvim = Nvim.from_session(session)

    if nvim.version.api_level < 1:
        sys.stderr.write("This version of pynvim "
                         "requires nvim 0.1.6 or later")
        sys.exit(1)

    host = Host(nvim)
    host.start(plugins)


@overload
def attach(session_type: Literal['tcp'], address: str, port: int = 7450) -> Nvim: ...


@overload
def attach(session_type: Literal['socket'], *, path: str) -> Nvim: ...


@overload
def attach(session_type: Literal['child'], *, argv: List[str]) -> Nvim: ...


@overload
def attach(session_type: Literal['stdio']) -> Nvim: ...


def attach(
    session_type: TTransportType,
    address: Optional[str] = None,
    port: int = 7450,
    path: Optional[str] = None,
    argv: Optional[List[str]] = None,
    decode: Literal[True] = True
) -> Nvim:
    """Provide a nicer interface to create python api sessions.

    Previous machinery to create python api sessions is still there. This only
    creates a facade function to make things easier for the most usual cases.
    Thus, instead of:
        from pynvim import socket_session, Nvim
        session = tcp_session(address=<address>, port=<port>)
        nvim = Nvim.from_session(session)
    You can now do:
        from pynvim import attach
        nvim = attach('tcp', address=<address>, port=<port>)
    And also:
        nvim = attach('socket', path=<path>)
        nvim = attach('child', argv=<argv>)
        nvim = attach('stdio')

    When the session is not needed anymore, it is recommended to explicitly
    close it:
       nvim.close()
    It is also possible to use the session as a context manager:
       with attach('socket', path=thepath) as nvim:
           print(nvim.funcs.getpid())
           print(nvim.current.line)
    This will automatically close the session when you're done with it, or
    when an error occurred.


    """
    session = (
        tcp_session(cast(str, address), port) if session_type == 'tcp' else
        socket_session(cast(str, path)) if session_type == 'socket' else
        stdio_session() if session_type == 'stdio' else
        child_session(cast(List[str], argv)) if session_type == 'child' else
        None
    )

    if not session:
        raise Exception('Unknown session type "%s"' % session_type)

    return Nvim.from_session(session).with_decode(decode)


def setup_logging(name: str) -> None:
    """Setup logging according to environment variables."""
    logger = logging.getLogger(__name__)
    if 'NVIM_PYTHON_LOG_FILE' in os.environ:
        prefix = os.environ['NVIM_PYTHON_LOG_FILE'].strip()
        major_version = sys.version_info[0]
        logfile = '{}_py{}_{}'.format(prefix, major_version, name)
        handler = logging.FileHandler(logfile, 'w', 'utf-8')
        handler.formatter = logging.Formatter(
            '%(asctime)s [%(levelname)s @ '
            '%(filename)s:%(funcName)s:%(lineno)s] %(process)s - %(message)s')
        logging.root.addHandler(handler)
        level = logging.INFO
        env_log_level = os.environ.get('NVIM_PYTHON_LOG_LEVEL', None)
        if env_log_level is not None:
            lvl = getattr(logging, env_log_level.strip(), None)
            if isinstance(lvl, int):
                level = lvl
            else:
                logger.warning('Invalid NVIM_PYTHON_LOG_LEVEL: %r, using INFO.',
                               env_log_level)
        logger.setLevel(level)