Codebase list python-pynvim / 0ec5ff5
New upstream release. Debian Janitor 2 years ago
38 changed file(s) with 1213 addition(s) and 116 deletion(s). Raw diff Collapse all Expand all
0 [run]
1 source = .
2 branch = true
3 parallel = 1
4
5 [report]
6 show_missing = true
7 include = pynvim/*,test/*
0 *.egg-info/
1 __pycache__
2 neovim/ui/screen.c
3 neovim/ui/screen.so
4 build/
5 dist/
6 *.pyc
7 .cache
8 .eggs
9 .tox
10
11 # Sphinx documentation
12 docs/_build/
0 build:
1 image: latest
2
3 python:
4 version: 3.6
5 setup_py_install: true
0 dist: xenial
1 language: python
2 env:
3 global:
4 - PYTEST_ADDOPTS="-vv --cov-append"
5 matrix:
6 - CI_TARGET=tests
7 matrix:
8 include:
9 - python: pypy
10 dist: trusty
11 sudo: false
12 - python: 3.6
13 env: CI_TARGET=checkqa TOXENV=checkqa,docs
14 python:
15 - 2.7
16 - 3.4
17 - 3.5
18 - 3.6
19 - 3.7
20 - 3.8
21 install:
22 - if [ $CI_TARGET = tests ]; then
23 eval "$(curl -Ss https://raw.githubusercontent.com/neovim/bot-ci/master/scripts/travis-setup.sh) nightly-x64";
24 pip install -q tox-travis;
25 else
26 pip install -q tox;
27 fi
28 script:
29 - tox
30 after_script:
31 - if [ $CI_TARGET = tests ]; then
32 set -x;
33 pip install coverage;
34 coverage combine;
35 coverage report -m;
36 coverage xml;
37 bash <(curl --retry 5 --silent --fail https://codecov.io/bash) -f coverage.xml;
38 set +x;
39 fi
00 Metadata-Version: 2.1
11 Name: pynvim
2 Version: 0.4.2
2 Version: 0.4.3
33 Summary: Python client to neovim
44 Home-page: http://github.com/neovim/pynvim
55 Author: Thiago de Arruda
66 Author-email: tpadilha84@gmail.com
77 License: Apache
8 Download-URL: https://github.com/neovim/pynvim/archive/0.4.2.tar.gz
8 Download-URL: https://github.com/neovim/pynvim/archive/0.4.3.tar.gz
99 Description: UNKNOWN
1010 Platform: UNKNOWN
1111 Provides-Extra: pyuv
0 coverage:
1 status:
2 project: true
3 patch: true
4 changes: true
5 comment: false
0 python-pynvim (0.4.2-2) UNRELEASED; urgency=medium
0 python-pynvim (0.4.3-1) UNRELEASED; urgency=medium
11
22 * Bump debhelper from old 12 to 13.
33 * Remove obsolete field Name from debian/upstream/metadata (already present in
44 machine-readable debian/copyright).
55 * Update standards version to 4.5.1, no changes needed.
6
7 -- Debian Janitor <janitor@jelmer.uk> Mon, 23 Aug 2021 22:50:02 -0000
6 * New upstream release.
7
8 -- Debian Janitor <janitor@jelmer.uk> Mon, 04 Oct 2021 02:16:03 -0000
89
910 python-pynvim (0.4.2-1) unstable; urgency=medium
1011
0 # Minimal makefile for Sphinx documentation
1 #
2
3 # You can set these variables from the command line.
4 SPHINXOPTS =
5 SPHINXBUILD = sphinx-build
6 SPHINXPROJ = Neovim
7 SOURCEDIR = .
8 BUILDDIR = _build
9
10 # Put it first so that "make" without argument is like "make help".
11 help:
12 @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
13
14 .PHONY: help Makefile
15
16 # Catch-all target: route all unknown targets to Sphinx using the new
17 # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
18 %: Makefile
19 @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
0 Buffer Class
1 ============
2
3 .. autoclass:: pynvim.api.Buffer
4 :members:
0 Nvim Class
1 ==========
2
3 An instance of this class is used by remote plugins.
4
5 .. autoclass:: pynvim.api.Nvim
6 :members:
0 Tabpage Class
1 =============
2
3 .. autoclass:: pynvim.api.Tabpage
4 :members:
0 Window Class
1 ============
2
3 .. autoclass:: pynvim.api.Window
4 :members:
0 #!/usr/bin/env python3
1 # -*- coding: utf-8 -*-
2 #
3 # Neovim documentation build configuration file, created by
4 # sphinx-quickstart on Sat Feb 3 12:15:22 2018.
5 #
6 # This file is execfile()d with the current directory set to its
7 # containing dir.
8 #
9 # Note that not all possible configuration values are present in this
10 # autogenerated file.
11 #
12 # All configuration values have a default; values that are commented out
13 # serve to show the default.
14
15 # If extensions (or modules to document with autodoc) are in another directory,
16 # add these directories to sys.path here. If the directory is relative to the
17 # documentation root, use os.path.abspath to make it absolute, like shown here.
18 #
19 # import os
20 # import sys
21 # sys.path.insert(0, os.path.abspath('./'))
22 import datetime
23
24
25 # -- General configuration ------------------------------------------------
26
27 # If your documentation needs a minimal Sphinx version, state it here.
28 #
29 # needs_sphinx = '1.0'
30
31 # Add any Sphinx extension module names here, as strings. They can be
32 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
33 # ones.
34 extensions = ['sphinx.ext.autodoc',
35 'sphinx.ext.viewcode']
36
37 # Add any paths that contain templates here, relative to this directory.
38 templates_path = ['_templates']
39
40 # The suffix(es) of source filenames.
41 # You can specify multiple suffix as a list of string:
42 #
43 # source_suffix = ['.rst', '.md']
44 source_suffix = '.rst'
45
46 # The master toctree document.
47 master_doc = 'index'
48
49 # General information about the project.
50 project = 'Neovim Python Client'
51 copyright = '2014 - {year}, Neovim'.format(
52 year=datetime.datetime.now().year
53 )
54 author = 'Neovim'
55
56 # The version info for the project you're documenting, acts as replacement for
57 # |version| and |release|, also used in various other places throughout the
58 # built documents.
59 #
60 # The short X.Y version.
61 version = ''
62 # The full version, including alpha/beta/rc tags.
63 release = ''
64
65 # The language for content autogenerated by Sphinx. Refer to documentation
66 # for a list of supported languages.
67 #
68 # This is also used if you do content translation via gettext catalogs.
69 # Usually you set "language" from the command line for these cases.
70 language = None
71
72 # List of patterns, relative to source directory, that match files and
73 # directories to ignore when looking for source files.
74 # This patterns also effect to html_static_path and html_extra_path
75 exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
76
77 # The name of the Pygments (syntax highlighting) style to use.
78 pygments_style = 'sphinx'
79
80 # If true, `todo` and `todoList` produce output, else they produce nothing.
81 todo_include_todos = False
82
83
84 # -- Options for HTML output ----------------------------------------------
85
86 # The theme to use for HTML and HTML Help pages. See the documentation for
87 # a list of builtin themes.
88 #
89 html_theme = 'sphinx_rtd_theme'
90
91 # Theme options are theme-specific and customize the look and feel of a theme
92 # further. For a list of options available for each theme, see the
93 # documentation.
94 #
95 # html_theme_options = {}
96
97 # Add any paths that contain custom static files (such as style sheets) here,
98 # relative to this directory. They are copied after the builtin static files,
99 # so a file named "default.css" will overwrite the builtin "default.css".
100 html_static_path = []
101
102
103 # -- Options for HTMLHelp output ------------------------------------------
104
105 # Output file base name for HTML help builder.
106 htmlhelp_basename = 'Neovimdoc'
107
108
109 # -- Options for LaTeX output ---------------------------------------------
110
111 latex_elements = {
112 # The paper size ('letterpaper' or 'a4paper').
113 #
114 # 'papersize': 'letterpaper',
115
116 # The font size ('10pt', '11pt' or '12pt').
117 #
118 # 'pointsize': '10pt',
119
120 # Additional stuff for the LaTeX preamble.
121 #
122 # 'preamble': '',
123
124 # Latex figure (float) alignment
125 #
126 # 'figure_align': 'htbp',
127 }
128
129 # Grouping the document tree into LaTeX files. List of tuples
130 # (source start file, target name, title,
131 # author, documentclass [howto, manual, or own class]).
132 latex_documents = [
133 (master_doc, 'Neovim.tex', 'Neovim Documentation',
134 'Neovim', 'manual'),
135 ]
136
137
138 # -- Options for manual page output ---------------------------------------
139
140 # One entry per manual page. List of tuples
141 # (source start file, name, description, authors, manual section).
142 man_pages = [
143 (master_doc, 'neovim', 'Neovim Documentation',
144 [author], 1)
145 ]
146
147
148 # -- Options for Texinfo output -------------------------------------------
149
150 # Grouping the document tree into Texinfo files. List of tuples
151 # (source start file, target name, title, author,
152 # dir menu entry, description, category)
153 texinfo_documents = [
154 (master_doc, 'Neovim', 'Neovim Documentation',
155 author, 'Neovim', 'One line description of project.',
156 'Miscellaneous'),
157 ]
158
159
160
0 Development
1 ===========
2
3 If you change the code, you need to run::
4
5 pip2 install .
6 pip3 install .
7
8 for the changes to have effect.
9 Alternatively you could execute Neovim with the ``$PYTHONPATH`` environment variable::
10
11 PYTHONPATH=/path/to/pynvim nvim
12
13 But note this is not completely reliable,
14 as installed packages can appear before ``$PYTHONPATH`` in the python search path.
15
16 You need to rerun this command if you have changed the code,
17 in order for Neovim to use it for the plugin host.
18
19 To run the tests execute::
20
21 python -m pytest
22
23 This will run the tests in an embedded instance of Neovim, with the current
24 directory added to ``sys.path``.
25
26 If you want to test a different version than ``nvim`` in ``$PATH`` use::
27
28 NVIM_CHILD_ARGV='["/path/to/nvim", "-u", "NONE", "--embed", "--headless"]' pytest
29
30 Alternatively, if you want to see the state of nvim, you could use::
31
32 export NVIM_LISTEN_ADDRESS=/tmp/nvimtest
33 xterm -e "nvim -u NONE"&
34 python -m pytest
35
36 But note you need to restart Neovim every time you run the tests!
37 Substitute your favorite terminal emulator for ``xterm``.
38
39 Troubleshooting
40 ---------------
41
42 You can run the plugin host in Neovim with logging enabled to debug errors::
43
44 NVIM_PYTHON_LOG_FILE=logfile NVIM_PYTHON_LOG_LEVEL=DEBUG nvim
45
46 As more than one Python host process might be started,
47 the log filenames take the pattern ``logfile_pyX_KIND``
48 where ``X`` is the major python version (2 or 3)
49 and ``KIND`` is either "rplugin" or "script" (for the ``:python[3]`` script interface).
50
51 If the host cannot start at all,
52 the error could be found in ``~/.nvimlog`` if ``nvim`` was compiled with logging.
53
54 Usage through the Python REPL
55 -----------------------------
56
57 A number of different transports are supported,
58 but the simplest way to get started is with the python REPL.
59 First, start Neovim with a known address (or use the ``$NVIM_LISTEN_ADDRESS`` of a running instance)::
60
61 NVIM_LISTEN_ADDRESS=/tmp/nvim nvim
62
63 In another terminal,
64 connect a python REPL to Neovim (note that the API is similar to the one exposed by the `python-vim bridge`_):
65
66 .. code-block:: python
67
68 >>> from pynvim import attach
69 # Create a python API session attached to unix domain socket created above:
70 >>> nvim = attach('socket', path='/tmp/nvim')
71 # Now do some work.
72 >>> buffer = nvim.current.buffer # Get the current buffer
73 >>> buffer[0] = 'replace first line'
74 >>> buffer[:] = ['replace whole buffer']
75 >>> nvim.command('vsplit')
76 >>> nvim.windows[1].width = 10
77 >>> nvim.vars['global_var'] = [1, 2, 3]
78 >>> nvim.eval('g:global_var')
79 [1, 2, 3]
80
81 .. _`python-vim bridge`: http://vimdoc.sourceforge.net/htmldoc/if_pyth.html#python-vim
82
83 You can embed Neovim into your python application instead of binding to a running neovim instance:
84
85 .. code-block:: python
86
87 >>> from pynvim import attach
88 >>> nvim = attach('child', argv=["/bin/env", "nvim", "--embed", "--headless"])
89
90 The tests can be consulted for more examples.
0 Neovim Python Client
1 ====================
2
3 Implements support for Python plugins in `Neovim`_.
4 Also works as a library for connecting to and scripting Neovim processes through its msgpack-rpc API.
5
6 .. _`Neovim`: http://neovim.io/
7
8 .. toctree::
9 :caption: Getting started
10 :maxdepth: 2
11
12 installation
13
14 .. toctree::
15 :caption: Usage
16 :maxdepth: 2
17
18 usage/python-plugin-api
19 usage/remote-plugins
20
21 .. toctree::
22 :caption: API documentation
23 :maxdepth: 2
24
25 plugin-decorators
26 api/nvim
27 api/buffer
28 api/window
29 api/tabpage
30
31 .. toctree::
32 :caption: Development
33 :maxdepth: 2
34
35 development
0 Installation
1 ============
2
3 The Neovim Python client supports Python 2.7, and 3.4 or later.
4
5 Using pip
6 ---------
7
8 You can install the package without being root by adding the ``--user`` flag::
9
10 pip2 install --user pynvim
11 pip3 install --user pynvim
12
13 .. note::
14
15 If you only use one of python2 or python3,
16 it is enough to install that version.
17
18 If you follow Neovim HEAD, make sure to upgrade ``pynvim`` when you upgrade
19 Neovim::
20
21 pip2 install --upgrade pynvim
22 pip3 install --upgrade pynvim
23
24 Install from source
25 -------------------
26
27 Clone the repository somewhere on your disk and enter to the repository::
28
29 git clone https://github.com/neovim/pynvim.git
30 cd pynvim
31
32 Now you can install it on your system::
33
34 pip2 install .
35 pip3 install .
0 @ECHO OFF
1
2 pushd %~dp0
3
4 REM Command file for Sphinx documentation
5
6 if "%SPHINXBUILD%" == "" (
7 set SPHINXBUILD=sphinx-build
8 )
9 set SOURCEDIR=.
10 set BUILDDIR=_build
11 set SPHINXPROJ=Neovim
12
13 if "%1" == "" goto help
14
15 %SPHINXBUILD% >NUL 2>NUL
16 if errorlevel 9009 (
17 echo.
18 echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 echo.installed, then set the SPHINXBUILD environment variable to point
20 echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 echo.may add the Sphinx directory to PATH.
22 echo.
23 echo.If you don't have Sphinx installed, grab it from
24 echo.http://sphinx-doc.org/
25 exit /b 1
26 )
27
28 %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
29 goto end
30
31 :help
32 %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
33
34 :end
35 popd
0 Plugin Decorators
1 =================
2
3 .. module:: pynvim.plugin
4
5 Plugin decorators.
6
7 Plugin
8 ------
9
10 .. autofunction:: plugin
11
12 Command
13 -------
14
15 .. autofunction:: command
16
17 Autocmd
18 -------
19
20 .. autofunction:: autocmd
21
22 Function
23 --------
24
25 .. autofunction:: function
0 Python Plugin API
1 =================
2
3 Neovim has a new mechanism for defining plugins,
4 as well as a number of extensions to the python API.
5 The API extensions are accessible no matter if the traditional ``:python`` interface or the new mechanism is used,
6 as discussed on :doc:`remote-plugins`.
7
8 Nvim API methods: ``vim.api``
9 -----------------------------
10
11 Exposes Neovim API methods.
12 For instance to call ``nvim_strwidth``:
13
14 .. code-block:: python
15
16 result = vim.api.strwidth("some text")
17
18 Note the initial ``nvim_`` is not included.
19 Also, object methods can be called directly on their object:
20
21 .. code-block:: python
22
23 buf = vim.current.buffer
24 length = buf.api.line_count()
25
26 calls ``nvim_buf_line_count``.
27 Alternatively msgpack requests can be invoked directly:
28
29 .. code-block:: python
30
31 result = vim.request("nvim_strwith", "some text")
32 length = vim.request("nvim_buf_line_count", buf)
33
34 Both ``vim.api`` and ``vim.request`` can take an ``async_=True`` keyword argument
35 to instead send a msgpack notification. Nvim will execute the API method the
36 same way, but python will not wait for it to finish, so the return value is
37 unavailable.
38
39 Vimscript functions: ``vim.funcs``
40 ----------------------------------
41
42 Exposes vimscript functions (both builtin and global user defined functions) as a python namespace.
43 For instance to set the value of a register:
44
45 .. code-block:: python
46
47 vim.funcs.setreg('0', ["some", "text"], 'l')
48
49 These functions can also take the ``async_=True`` keyword argument, just like API
50 methods.
51
52 Lua integration
53 ---------------
54
55 Python plugins can define and invoke lua code in Nvim's in-process lua
56 interpreter. This is especially useful in asynchronous contexts, where an async
57 event handler can schedule a complex operation with many api calls to be
58 executed by nvim without interleaved processing of user input or other event
59 sources (unless requested).
60
61 The recommended usage is the following pattern. First use ``vim.exec_lua(code)``
62 to define a module with lua functions:
63
64 .. code-block:: python
65
66 vim.exec_lua("""
67 local a = vim.api
68 local function add(a,b)
69 return a+b
70 end
71
72 local function buffer_ticks()
73 local ticks = {}
74 for _, buf in ipairs(a.nvim_list_bufs()) do
75 ticks[#ticks+1] = a.nvim_buf_get_changedtick(buf)
76 end
77 return ticks
78 end
79
80 _testplugin = {add=add, buffer_ticks=buffer_ticks}
81 """)
82
83 Alternatively, place the code in ``/lua/testplugin.lua`` under your plugin repo
84 root, and use ``vim.exec_lua("_testplugin = require('testplugin')")``.
85 In both cases, replace ``testplugin`` with a unique string based on your plugin
86 name.
87
88 Then, the module can be acessed as ``vim.lua._testplugin``.
89
90 .. code-block:: python
91
92 mod = vim.lua._testplugin
93 mod.add(2,3) # => 5
94 mod.buffer_ticks() # => list of ticks
95
96 These functions can also take the ``async_=True`` keyword argument, just like API
97 methods.
98
99 It is also possible to pass arguments directly to a code block. Using
100 ``vim.exec_lua(code, args...)``, the arguments will be available in lua as ``...``.
101
102 Async calls
103 -----------
104
105 The API is not thread-safe in general.
106 However, ``vim.async_call`` allows a spawned thread to schedule code to be executed on the main thread.
107 This method could also be called from ``:python`` or a synchronous request handler,
108 to defer some execution that shouldn't block Neovim:
109
110 .. code-block:: vim
111
112 :python vim.async_call(myfunc, args...)
113
114 Note that this code will still block the plugin host if it does long-running computations.
115 Intensive computations should be done in a separate thread (or process),
116 and ``vim.async_call`` can be used to send results back to Neovim.
117
118 Some methods accept an ``async_`` keyword argument: ``vim.eval``,
119 ``vim.command``, ``vim.request`` as well as the ``vim.funcs``, ``vim.api` and
120 ``vim.lua``` wrappers. When ``async_=True`` is passed the client will not wait
121 for Neovim to complete the request (which also means that the return value is
122 unavailable).
0 .. _remote-plugins:
1
2 Remote (new-style) plugins
3 ==========================
4
5 Neovim allows Python 3 plugins to be defined by placing python files or packages in ``rplugin/python3/`` (in a ``runtimepath`` folder).
6 Python 2 rplugins are also supported and placed in ``rplugin/python/``,
7 but are considered deprecated.
8 Further added library features will only be available on Python 3.
9 Rplugins follow the structure of this example:
10
11 .. code-block:: python
12
13 import pynvim
14
15 @pynvim.plugin
16 class TestPlugin(object):
17
18 def __init__(self, nvim):
19 self.nvim = nvim
20
21 @pynvim.function('TestFunction', sync=True)
22 def testfunction(self, args):
23 return 3
24
25 @pynvim.command('TestCommand', nargs='*', range='')
26 def testcommand(self, args, range):
27 self.nvim.current.line = ('Command with args: {}, range: {}'
28 .format(args, range))
29
30 @pynvim.autocmd('BufEnter', pattern='*.py', eval='expand("<afile>")', sync=True)
31 def on_bufenter(self, filename):
32 self.nvim.out_write('testplugin is in ' + filename + '\n')
33
34 If ``sync=True`` is supplied Neovim will wait for the handler to finish
35 (this is required for function return values),
36 but by default handlers are executed asynchronously.
37
38 Normally async handlers (``sync=False``, the default)
39 are blocked while a synchronous handler is running.
40 This ensures that async handlers can call requests without Neovim confusing these requests with requests from a synchronous handler.
41 To execute an asynchronous handler even when other handlers are running,
42 add ``allow_nested=True`` to the decorator.
43 This handler must then not make synchronous Neovim requests,
44 but it can make asynchronous requests, i.e. passing ``async_=True``.
45
46 .. note::
47
48 Plugin objects are constructed the first time any request of the class is
49 invoked. Any error in ``__init__`` will be reported as an error from this
50 first request. A well-behaved rplugin will not start executing until its
51 functionality is requested by the user. Initialize the plugin when user
52 invokes a command, or use an appropriate autocommand, e.g. FileType if it
53 makes sense to automatically start the plugin for a given filetype. Plugins
54 must not invoke API methods (or really do anything with non-trivial
55 side-effects) in global module scope, as the module might be loaded as part
56 of executing `UpdateRemotePlugins`.
57
58 You need to run ``:UpdateRemotePlugins`` in Neovim for changes in the specifications to have effect.
59 For details see ``:help remote-plugin`` in Neovim.
60
61 For local plugin development, it's a good idea to use an isolated vimrc:
62
63 .. code-block:: console
64
65 cat vimrc
66 let &runtimepath.=','.escape(expand('<sfile>:p:h'), '\,')
67
68 That appends the current directory to the Nvim runtime path so Nvim can
69 find your plugin. You can now invoke Neovim:
70
71 .. code-block:: console
72
73 nvim -u ./vimrc
74
75 Then run ``:UpdateRemotePlugins`` and your plugin should be activated.
76
77 In case you run into some issues, you can list your loaded plugins from inside
78 Neovim by running ``:scriptnames`` like so.:
79
80 .. code-block:: vim
81
82 :scriptnames
83 1: ~/path/to/your/plugin-git-repo/vimrc
84 2: /usr/share/nvim/runtime/filetype.vim
85 ...
86 25: /usr/share/nvim/runtime/plugin/zipPlugin.vim
87 26: ~/path/to/your/plugin-git-repo/plugin/lucid.vim
88
89 You can also inspect the ``&runtimepath`` like this:
90
91 .. code-block:: vim
92
93 :set runtimepath
94 runtimepath=~/.config/nvim,/etc/xdg/nvim,~/.local/share/nvim/site,...,
95 ,~/g/path/to/your/plugin-git-repo
96
97 " Or alternatively
98 :echo &rtp
0 from setuptools import setup
1
2 setup(name='neovim',
3 version='0.3.1',
4 description='Transition packgage for pynvim',
5 url='http://github.com/neovim/python-client',
6 author='Thiago de Arruda',
7 author_email='tpadilha84@gmail.com',
8 license='Apache',
9 packages=[],
10 install_requires=['pynvim>=0.3.1'],
11 zip_safe=False)
7979 self._handlers.get(msg[0], self._on_invalid_message)(msg)
8080 except Exception:
8181 err_str = format_exc(5)
82 pass # replaces next logging statement
83 #warn(err_str)
82 warn(err_str)
8483 self._msgpack_stream.send([1, 0, err_str, None])
8584
8685 def _on_request(self, msg):
8887 # - msg[1]: id
8988 # - msg[2]: method name
9089 # - msg[3]: arguments
91 pass # replaces next logging statement
92 #debug('received request: %s, %s', msg[2], msg[3])
90 debug('received request: %s, %s', msg[2], msg[3])
9391 self._request_cb(msg[2], msg[3], Response(self._msgpack_stream,
9492 msg[1]))
9593
9896 # - msg[1]: the id
9997 # - msg[2]: error(if any)
10098 # - msg[3]: result(if not errored)
101 pass # replaces next logging statement
102 #debug('received response: %s, %s', msg[2], msg[3])
99 debug('received response: %s, %s', msg[2], msg[3])
103100 self._pending_requests.pop(msg[1])(msg[2], msg[3])
104101
105102 def _on_notification(self, msg):
106103 # notification/event
107104 # - msg[1]: event name
108105 # - msg[2]: arguments
109 pass # replaces next logging statement
110 #debug('received notification: %s, %s', msg[1], msg[2])
106 debug('received notification: %s, %s', msg[1], msg[2])
111107 self._notification_cb(msg[1], msg[2])
112108
113109 def _on_invalid_message(self, msg):
114110 error = 'Received invalid message %s' % msg
115 pass # replaces next logging statement
116 #warn(error)
111 warn(error)
117112 self._msgpack_stream.send([1, 0, error, None])
118113
119114
139134 resp = [1, self._request_id, value, None]
140135 else:
141136 resp = [1, self._request_id, None, value]
142 pass # replaces next logging statement
143 #debug('sending response to request %d: %s', self._request_id, resp)
137 debug('sending response to request %d: %s', self._request_id, resp)
144138 self._msgpack_stream.send(resp)
101101 pipe = sys.stdin
102102 coroutine = self._loop.connect_read_pipe(self._fact, pipe)
103103 self._loop.run_until_complete(coroutine)
104 pass # replaces next logging statement
105 #debug("native stdin connection successful")
104 debug("native stdin connection successful")
106105
107106 # Make sure subprocesses don't clobber stdout,
108107 # send the output to stderr instead.
115114 pipe = os.fdopen(rename_stdout, 'wb')
116115 coroutine = self._loop.connect_write_pipe(self._fact, pipe)
117116 self._loop.run_until_complete(coroutine)
118 pass # replaces next logging statement
119 #debug("native stdout connection successful")
117 debug("native stdout connection successful")
120118
121119 def _connect_child(self, argv):
122120 if os.name != 'nt':
9494
9595 def connect_tcp(self, address, port):
9696 """Connect to tcp/ip `address`:`port`. Delegated to `_connect_tcp`."""
97 pass # replaces next logging statement
98 #info('Connecting to TCP address: %s:%d', address, port)
97 info('Connecting to TCP address: %s:%d', address, port)
9998 self._connect_tcp(address, port)
10099
101100 def connect_socket(self, path):
102101 """Connect to socket at `path`. Delegated to `_connect_socket`."""
103 pass # replaces next logging statement
104 #info('Connecting to %s', path)
102 info('Connecting to %s', path)
105103 self._connect_socket(path)
106104
107105 def connect_stdio(self):
108106 """Connect using stdin/stdout. Delegated to `_connect_stdio`."""
109 pass # replaces next logging statement
110 #info('Preparing stdin/stdout for streaming data')
107 info('Preparing stdin/stdout for streaming data')
111108 self._connect_stdio()
112109
113110 def connect_child(self, argv):
114111 """Connect a new Nvim instance. Delegated to `_connect_child`."""
115 pass # replaces next logging statement
116 #info('Spawning a new nvim instance')
112 info('Spawning a new nvim instance')
117113 self._connect_child(argv)
118114
119115 def send(self, data):
120116 """Queue `data` for sending to Nvim."""
121 pass # replaces next logging statement
122 #debug("Sending '%s'", data)
117 debug("Sending '%s'", data)
123118 self._send(data)
124119
125120 def threadsafe_call(self, fn):
144139 self._on_data = data_cb
145140 if threading.current_thread() == main_thread:
146141 self._setup_signals([signal.SIGINT, signal.SIGTERM])
147 pass # replaces next logging statement
148 #debug('Entering event loop')
142 debug('Entering event loop')
149143 self._run()
150 pass # replaces next logging statement
151 #debug('Exited event loop')
144 debug('Exited event loop')
152145 if threading.current_thread() == main_thread:
153146 self._teardown_signals()
154147 signal.signal(signal.SIGINT, default_int_handler)
157150 def stop(self):
158151 """Stop the event loop."""
159152 self._stop()
160 pass # replaces next logging statement
161 #debug('Stopped event loop')
153 debug('Stopped event loop')
162154
163155 def close(self):
164156 """Stop the event loop."""
165157 self._close()
166 pass # replaces next logging statement
167 #debug('Closed event loop')
158 debug('Closed event loop')
168159
169160 def _on_signal(self, signum):
170161 msg = 'Received {}'.format(self._signames[signum])
171 pass # replaces next logging statement
172 #debug(msg)
162 debug(msg)
173163 if signum == signal.SIGINT and self._transport_type == 'stdio':
174164 # When the transport is stdio, we are probably running as a Nvim
175165 # child process. In that case, we don't want to be killed by
182172 self.stop()
183173
184174 def _on_error(self, error):
185 pass # replaces next logging statement
186 #debug(error)
175 debug(error)
187176 self._error = OSError(error)
188177 self.stop()
189178
2929
3030 def send(self, msg):
3131 """Queue `msg` for sending to Nvim."""
32 pass # replaces next logging statement
33 #debug('sent %s', msg)
32 debug('sent %s', msg)
3433 self.loop.send(self._packer.pack(msg))
3534
3635 def run(self, message_cb):
5554 self._unpacker.feed(data)
5655 while True:
5756 try:
58 pass # replaces next logging statement
59 #debug('waiting for message...')
57 debug('waiting for message...')
6058 msg = next(self._unpacker)
61 pass # replaces next logging statement
62 #debug('received message: %s', msg)
59 debug('received message: %s', msg)
6360 self._message_cb(msg)
6461 except StopIteration:
65 pass # replaces next logging statement
66 #debug('unpacker needs more data...')
62 debug('unpacker needs more data...')
6763 break
3737 try:
3838 fn(*args, **kwargs)
3939 except Exception:
40 pass # replaces next logging statement
41 #warn("error caught while excecuting async callback\n%s\n",
42 #format_exc())
40 warn("error caught while excecuting async callback\n%s\n",
41 format_exc())
4342
4443 def greenlet_wrapper():
4544 gr = greenlet.greenlet(handler)
9897 raise OSError('EOF')
9998 err, rv = v
10099 if err:
101 pass # replaces next logging statement
102 #info("'Received error: %s", err)
100 info("'Received error: %s", err)
103101 raise self.error_wrapper(err)
104102 return rv
105103
128126 gr.switch()
129127
130128 if self._setup_exception:
131 pass # replaces next logging statement
132 #error('Setup error: {}'.format(self._setup_exception))
129 error('Setup error: {}'.format(self._setup_exception))
133130 raise self._setup_exception
134131
135132 # Process all pending requests and notifications
158155 parent = gr.parent
159156
160157 def response_cb(err, rv):
161 pass # replaces next logging statement
162 #debug('response is available for greenlet %s, switching back', gr)
158 debug('response is available for greenlet %s, switching back', gr)
163159 gr.switch(err, rv)
164160
165161 self._async_session.request(method, args, response_cb)
166 pass # replaces next logging statement
167 #debug('yielding from greenlet %s to wait for response', gr)
162 debug('yielding from greenlet %s to wait for response', gr)
168163 return parent.switch()
169164
170165 def _blocking_request(self, method, args):
197192 def handler():
198193 try:
199194 rv = self._request_cb(name, args)
200 pass # replaces next logging statement
201 #debug('greenlet %s finished executing, '
202 #+ 'sending %s as response', gr, rv)
195 debug('greenlet %s finished executing, '
196 + 'sending %s as response', gr, rv)
203197 response.send(rv)
204198 except ErrorResponse as err:
205 pass # replaces next logging statement
206 #warn("error response from request '%s %s': %s", name,
207 #args, format_exc())
199 warn("error response from request '%s %s': %s", name,
200 args, format_exc())
208201 response.send(err.args[0], error=True)
209202 except Exception as err:
210 pass # replaces next logging statement
211 #warn("error caught while processing request '%s %s': %s", name,
212 #args, format_exc())
203 warn("error caught while processing request '%s %s': %s", name,
204 args, format_exc())
213205 response.send(repr(err) + "\n" + format_exc(5), error=True)
214 pass # replaces next logging statement
215 #debug('greenlet %s is now dying...', gr)
206 debug('greenlet %s is now dying...', gr)
216207
217208 # Create a new greenlet to handle the request
218209 gr = greenlet.greenlet(handler)
219 pass # replaces next logging statement
220 #debug('received rpc request, greenlet %s will handle it', gr)
210 debug('received rpc request, greenlet %s will handle it', gr)
221211 gr.switch()
222212
223213 def _on_notification(self, name, args):
224214 def handler():
225215 try:
226216 self._notification_cb(name, args)
227 pass # replaces next logging statement
228 #debug('greenlet %s finished executing', gr)
217 debug('greenlet %s finished executing', gr)
229218 except Exception:
230 pass # replaces next logging statement
231 #warn("error caught while processing notification '%s %s': %s",
232 #name, args, format_exc())
233
234 pass # replaces next logging statement
235 #debug('greenlet %s is now dying...', gr)
219 warn("error caught while processing notification '%s %s': %s",
220 name, args, format_exc())
221
222 debug('greenlet %s is now dying...', gr)
236223
237224 gr = greenlet.greenlet(handler)
238 pass # replaces next logging statement
239 #debug('received rpc notification, greenlet %s will handle it', gr)
225 debug('received rpc notification, greenlet %s will handle it', gr)
240226 gr.switch()
241227
242228
116116 handler = self._request_handlers.get(name, None)
117117 if not handler:
118118 msg = self._missing_handler_error(name, 'request')
119 pass # replaces next logging statement
120 #error(msg)
119 error(msg)
121120 raise ErrorResponse(msg)
122121
123 pass # replaces next logging statement
124 #debug('calling request handler for "%s", args: "%s"', name, args)
122 debug('calling request handler for "%s", args: "%s"', name, args)
125123 rv = handler(*args)
126 pass # replaces next logging statement
127 #debug("request handler for '%s %s' returns: %s", name, args, rv)
124 debug("request handler for '%s %s' returns: %s", name, args, rv)
128125 return rv
129126
130127 def _on_notification(self, name, args):
134131 handler = self._notification_handlers.get(name, None)
135132 if not handler:
136133 msg = self._missing_handler_error(name, 'notification')
137 pass # replaces next logging statement
138 #error(msg)
134 error(msg)
139135 self._on_async_err(msg + "\n")
140136 return
141137
142 pass # replaces next logging statement
143 #debug('calling notification handler for "%s", args: "%s"', name, args)
138 debug('calling notification handler for "%s", args: "%s"', name, args)
144139 handler(*args)
145140
146141 def _missing_handler_error(self, name, kind):
157152 for path in plugins:
158153 err = None
159154 if path in self._loaded:
160 pass # replaces next logging statement
161 #error('{} is already loaded'.format(path))
155 error('{} is already loaded'.format(path))
162156 continue
163157 try:
164158 if path == "script_host.py":
172166 self._discover_classes(module, handlers, path)
173167 self._discover_functions(module, handlers, path, False)
174168 if not handlers:
175 pass # replaces next logging statement
176 #error('{} exports no handlers'.format(path))
169 error('{} exports no handlers'.format(path))
177170 continue
178171 self._loaded[path] = {'handlers': handlers, 'module': module}
179172 except Exception as e:
180173 err = ('Encountered {} loading plugin at {}: {}\n{}'
181174 .format(type(e).__name__, path, e, format_exc(5)))
182 pass # replaces next logging statement
183 #error(err)
175 error(err)
184176 self._load_errors[path] = err
185177
186178 kind = ("script-host" if len(plugins) == 1 and has_script
6565 forwarded to Nvim.
6666 """
6767 self.nvim = nvim
68 pass # replaces next logging statement
69 #info('install import hook/path')
68 info('install import hook/path')
7069 self.hook = path_hook(nvim)
7170 sys.path_hooks.append(self.hook)
7271 nvim.VIM_SPECIAL_PATH = '_vim_path_'
7372 sys.path.append(nvim.VIM_SPECIAL_PATH)
74 pass # replaces next logging statement
75 #info('redirect sys.stdout and sys.stderr')
73 info('redirect sys.stdout and sys.stderr')
7674 self.saved_stdout = sys.stdout
7775 self.saved_stderr = sys.stderr
7876 sys.stdout = RedirectStream(lambda data: nvim.out_write(data))
8179 def teardown(self):
8280 """Restore state modified from the `setup` call."""
8381 nvim = self.nvim
84 pass # replaces next logging statement
85 #info('uninstall import hook/path')
82 info('uninstall import hook/path')
8683 sys.path.remove(nvim.VIM_SPECIAL_PATH)
8784 sys.path_hooks.remove(self.hook)
88 pass # replaces next logging statement
89 #info('restore sys.stdout and sys.stderr')
85 info('restore sys.stdout and sys.stderr')
9086 sys.stdout = self.saved_stdout
9187 sys.stderr = self.saved_stderr
9288
10399 def python_execute_file(self, file_path, range_start, range_stop):
104100 """Handle the `pyfile` ex command."""
105101 self._set_current_range(range_start, range_stop)
106 with open(file_path) as f:
102 with open(file_path, 'rb') as f:
107103 script = compile(f.read(), file_path, 'exec')
108104 try:
109105 exec(script, self.module.__dict__)
3838 return (name, VERSION.__dict__, type_, method_spec, attributes)
3939
4040
41 VERSION = Version(major=0, minor=4, patch=2, prerelease='')
41 VERSION = Version(major=0, minor=4, patch=3, prerelease='')
00 Metadata-Version: 2.1
11 Name: pynvim
2 Version: 0.4.2
2 Version: 0.4.3
33 Summary: Python client to neovim
44 Home-page: http://github.com/neovim/pynvim
55 Author: Thiago de Arruda
66 Author-email: tpadilha84@gmail.com
77 License: Apache
8 Download-URL: https://github.com/neovim/pynvim/archive/0.4.2.tar.gz
8 Download-URL: https://github.com/neovim/pynvim/archive/0.4.3.tar.gz
99 Description: UNKNOWN
1010 Platform: UNKNOWN
1111 Provides-Extra: pyuv
0 .coveragerc
1 .gitignore
2 .readthedocs.yml
3 .travis.yml
04 LICENSE
15 MANIFEST.in
26 README.md
7 codecov.yml
38 setup.cfg
49 setup.py
10 tox.ini
11 docs/Makefile
12 docs/conf.py
13 docs/development.rst
14 docs/index.rst
15 docs/installation.rst
16 docs/make.bat
17 docs/plugin-decorators.rst
18 docs/api/buffer.rst
19 docs/api/nvim.rst
20 docs/api/tabpage.rst
21 docs/api/window.rst
22 docs/usage/python-plugin-api.rst
23 docs/usage/remote-plugins.rst
24 dummy/setup.py
525 neovim/__init__.py
626 neovim/api/__init__.py
727 pynvim/__init__.py
3151 pynvim/plugin/decorators.py
3252 pynvim/plugin/host.py
3353 pynvim/plugin/script_host.py
54 scripts/disable_log_statements.sh
55 scripts/enable_log_statements.sh
56 scripts/logging_statement_modifier.py
57 scripts/publish.sh
3458 test/conftest.py
3559 test/test_buffer.py
3660 test/test_client_rpc.py
0 msgpack>=0.5.0
01 greenlet
1 msgpack>=0.5.0
22
33 [pyuv]
44 pyuv>=1.0.0
0 #!/bin/sh -e
1
2 cd pynvim
3 find -name '*.py' | xargs -i{} ../scripts/logging_statement_modifier.py {}
0 #!/bin/sh -e
1
2 cd pynvim
3 find -name '*.py' | xargs -i{} ../scripts/logging_statement_modifier.py --restore {}
0 #!/usr/bin/env python
1
2 """\
3 Logging Statement Modifier - replace logging calls with pass (or vice versa)
4 Author: David Underhill <dgu@cs.stanford.edu>
5 Version: 1.00 (06-Feb-2010)
6
7 This script parses a Python file and comments out logging statements, replacing
8 them with a pass statement (or vice versa). The purpose of commenting out these
9 statements is to improve performance. Even if logging is disabled, arguments to
10 logging method calls must still be evaluated, which can be expensive.
11
12 This tool handles most common cases:
13 * Log statements may span multiple lines.
14 * Custom logging levels may be added (LEVELS, LEVEL_VALUES).
15 * Integral logging levels & named logging levels (DEBUG, etc.) are recognized.
16 * Logging statements log(), debug(), ..., critical() are all recognized.
17 * Statements with unrecognized logging levels will be left as-is.
18 * 'logging' is the assumed logging module name (LOGGING_MODULE_NAME).
19
20 However, its ability to parse files is limited:
21 * It only operates on logging statements in the form logging.log(<level>, ...)
22 and logging.<level>(...).
23 * The <level> must either be an integral constant or contain one of the names
24 from the LEVELS constant below.
25 * If a logging statement is made, it is assumed that no other statement is
26 made on the same line as logging statement (except for statements made in
27 between the open and close parenthesis of the logging call). For example,
28 a semi-colon and then a second statement on the same line as a logging call
29 will not be handled properly.
30 * Logging methods must be called through SOME module, e.g., logging.log(), not
31 just log().
32 * For simplicity, undoing the commenting process relies on a comment left by
33 the program on the pass statements it adds when commenting out logging
34 statements. (So don't change the comment it outputs by the pass statement).
35
36 To run this command on all of the Python files in a particular folder and its
37 sub-folders at once, try this (replace '/path/to' as appropriate):
38 find . -name '*.py' | xargs -i{} /path/to/logging_statement_modifier.py {}
39 """
40
41 import logging
42 from optparse import OptionParser
43 import re
44 import sys
45
46 # logging level names and values
47 LEVELS = ['DEBUG', 'INFO', 'WARN', 'WARNING', 'ERROR', 'CRITICAL']
48 LEVEL_VALUES = [logging.DEBUG, logging.INFO, logging.WARN, logging.WARNING, logging.ERROR, logging.CRITICAL]
49 LEVELS_DICT = dict(zip(LEVELS, LEVEL_VALUES))
50
51 # names of methods in the logging module which perform logging
52 LOGGING_METHODS_OF_INTEREST = ['log', 'debug', 'info', 'warn', 'warning', 'error', 'critical']
53
54 # name of the logging module
55 LOGGING_MODULE_NAME = 'logging'
56
57 # this matches logging.<method>([<first_arg>,]
58 # STR_RE_LOGGING_CALL = r'%s.(\w+)[(](([^,\r\n]+),)?' % LOGGING_MODULE_NAME
59 STR_RE_LOGGING_CALL = r'\b(' + '|'.join(LOGGING_METHODS_OF_INTEREST) + r')[(](([^,\r\n]+),)?'
60
61 # contents of a pass line (not including prefixed whitespace)
62 PASS_LINE_CONTENTS = 'pass # replaces next logging statement\n'
63
64 # Match a logging call (must only be prefixed with whitespace). Capture groups
65 # include the whitespace, the logging method called, and the first argument if
66 # possible
67 RE_LOGGING_START = re.compile(r'^(\s+)' + STR_RE_LOGGING_CALL)
68 RE_LOGGING_START_IN_COMMENT = re.compile(r'^(\s+)#' + STR_RE_LOGGING_CALL)
69
70 def main(argv=sys.argv[1:]):
71 """Parses the command line comments."""
72 usage = 'usage: %prog [options] FILE\n\n' + __doc__
73 parser = OptionParser(usage)
74
75 # options
76 parser.add_option("-f", "--force",
77 action='store_true', default=False,
78 help="make changes even if they cannot undone before saving the new file")
79 parser.add_option("-m", "--min_level",
80 default='NONE',
81 help="minimum level of logging statements to modify [default: no minimum]")
82 parser.add_option("-M", "--max_level",
83 default='NONE',
84 help="maximum level of logging statements to modify [default: no maximum]")
85 parser.add_option("-o", "--output-file",
86 default=None,
87 help="where to output the result [default: overwrite the input file]")
88 parser.add_option("-r", "--restore",
89 action='store_true', default=False,
90 help="restore logging statements previously commented out and replaced with pass statements")
91 parser.add_option("-v", "--verbose",
92 action='store_true', default=False,
93 help="print informational messages about changes made")
94
95 (options, args) = parser.parse_args(argv)
96 if len(args) != 1:
97 parser.error("expected 1 argument but got %d arguments: %s" % (len(args), ' '.join(args)))
98 input_fn = args[0]
99 if not options.output_file:
100 options.output_file = input_fn
101
102 # validate min/max level
103 LEVEL_CHOICES = LEVELS + ['NONE']
104 min_level_value = 0 if options.min_level == 'NONE' else get_level_value(options.min_level)
105 if options.min_level is None:
106 parser.error("min level must be an integer or one of these values: %s" % ', '.join(LEVEL_CHOICES))
107 max_level_value = 9000 if options.max_level == 'NONE' else get_level_value(options.max_level)
108 if options.max_level is None:
109 parser.error("max level must be an integer or one of these values: %s" % ', '.join(LEVEL_CHOICES))
110
111 if options.verbose:
112 logging.getLogger().setLevel(logging.INFO)
113
114 try:
115 return modify_logging(input_fn, options.output_file,
116 min_level_value, max_level_value,
117 options.restore, options.force)
118 except OSError as e:
119 logging.error(str(e))
120 return -1
121
122 # matches two main groups: 1) leading whitespace and 2) all following text
123 RE_LINE_SPLITTER_COMMENT = re.compile(r'^(\s*)((.|\n)*)$')
124 def comment_lines(lines):
125 """Comment out the given list of lines and return them. The hash mark will
126 be inserted before the first non-whitespace character on each line."""
127 ret = []
128 for line in lines:
129 ws_prefix, rest, ignore = RE_LINE_SPLITTER_COMMENT.match(line).groups()
130 ret.append(ws_prefix + '#' + rest)
131 return ''.join(ret)
132
133 # matches two main groups: 1) leading whitespace and 2) all following text
134 RE_LINE_SPLITTER_UNCOMMENT = re.compile(r'^(\s*)#((.|\n)*)$')
135 def uncomment_lines(lines):
136 """Uncomment the given list of lines and return them. The first hash mark
137 following any amount of whitespace will be removed on each line."""
138 ret = []
139 for line in lines:
140 ws_prefix, rest, ignore = RE_LINE_SPLITTER_UNCOMMENT.match(line).groups()
141 ret.append(ws_prefix + rest)
142 return ''.join(ret)
143
144 def first_arg_to_level_name(arg):
145 """Decide what level the argument specifies and return it. The argument
146 must contain (case-insensitive) one of the values in LEVELS or be an integer
147 constant. Otherwise None will be returned."""
148 try:
149 return int(arg)
150 except ValueError:
151 arg = arg.upper()
152 for level in LEVELS:
153 if level in arg:
154 return level
155 return None
156
157 def get_level_value(level):
158 """Returns the logging value associated with a particular level name. The
159 argument must be present in LEVELS_DICT or be an integer constant.
160 Otherwise None will be returned."""
161 try:
162 # integral constants also work: they are the level value
163 return int(level)
164 except ValueError:
165 try:
166 return LEVELS_DICT[level.upper()]
167 except KeyError:
168 logging.warning("level '%s' cannot be translated to a level value (not present in LEVELS_DICT)" % level)
169 return None
170
171 def get_logging_level(logging_stmt, commented_out=False):
172 """Determines the level of logging in a given logging statement. The string
173 representing this level is returned. False is returned if the method is
174 not a logging statement and thus has no level. None is returned if a level
175 should have been found but wasn't."""
176 regexp = RE_LOGGING_START_IN_COMMENT if commented_out else RE_LOGGING_START
177 ret = regexp.match(logging_stmt)
178 _, method_name, _, first_arg = ret.groups()
179 if method_name not in LOGGING_METHODS_OF_INTEREST:
180 logging.debug('skipping uninteresting logging call: %s' % method_name)
181 return False
182
183 if method_name != 'log':
184 return method_name
185
186 # if the method name did not specify the level, we must have a first_arg to extract the level from
187 if not first_arg:
188 logging.warning("logging.log statement found but we couldn't extract the first argument")
189 return None
190
191 # extract the level of logging from the first argument to the log() call
192 level = first_arg_to_level_name(first_arg)
193 if level is None:
194 logging.warning("arg does not contain any known level '%s'\n" % first_arg)
195 return None
196 return level
197
198 def level_is_between(level, min_level_value, max_level_value):
199 """Returns True if level is between the specified min or max, inclusive."""
200 level_value = get_level_value(level)
201 if level_value is None:
202 # unknown level value
203 return False
204 return level_value >= min_level_value and level_value <= max_level_value
205
206 def split_call(lines, open_paren_line=0):
207 """Returns a 2-tuple where the first element is the list of lines from the
208 first open paren in lines to the matching closed paren. The second element
209 is all remaining lines in a list."""
210 num_open = 0
211 num_closed = 0
212 for i, line in enumerate(lines):
213 c = line.count('(')
214 num_open += c
215 if not c and i==open_paren_line:
216 raise Exception('Exception open parenthesis in line %d but there is not one there: %s' % (i, str(lines)))
217 num_closed += line.count(')')
218
219 if num_open == num_closed:
220 return (lines[:i+1], lines[i+1:])
221
222 print(''.join(lines))
223 raise Exception('parenthesis are mismatched (%d open, %d closed found)' % (num_open, num_closed))
224
225 def modify_logging(input_fn, output_fn, min_level_value, max_level_value, restore, force):
226 """Modifies logging statements in the specified file."""
227 # read in all the lines
228 logging.info('reading in %s' % input_fn)
229 fh = open(input_fn, 'r')
230 lines = fh.readlines()
231 fh.close()
232 original_contents = ''.join(lines)
233
234 if restore:
235 forwards = restore_logging
236 backwards = disable_logging
237 else:
238 forwards = disable_logging
239 backwards = restore_logging
240
241 # apply the requested action
242 new_contents = forwards(lines, min_level_value, max_level_value)
243
244 # quietly check to see if we can undo what we just did (if not, the text
245 # contains something we cannot translate [bug or limitation with this code])
246 logging.disable(logging.CRITICAL)
247 new_contents_undone = backwards(new_contents.splitlines(True), min_level_value, max_level_value)
248 logging.disable(logging.DEBUG)
249 if original_contents != new_contents_undone:
250 base_str = 'We are unable to revert this action as expected'
251 if force:
252 logging.warning(base_str + " but -f was specified so we'll do it anyway.")
253 else:
254 logging.error(base_str + ', so we will not do it in the first place. Pass -f to override this and make the change anyway.')
255 return -1
256
257 logging.info('writing the new contents to %s' % output_fn)
258 fh = open(output_fn, 'w')
259 fh.write(new_contents)
260 fh.close()
261 logging.info('done!')
262 return 0
263
264 def check_level(logging_stmt, logging_stmt_is_commented_out, min_level_value, max_level_value):
265 """Extracts the level of the logging statement and returns True if the
266 level falls betwen min and max_level_value. If the level cannot be
267 extracted, then a warning is logged."""
268 level = get_logging_level(logging_stmt, logging_stmt_is_commented_out)
269 if level is None:
270 logging.warning('skipping logging statement because the level could not be extracted: %s' % logging_stmt.strip())
271 return False
272 elif level is False:
273 return False
274 elif level_is_between(level, min_level_value, max_level_value):
275 return True
276 else:
277 logging.debug('keep this one as is (not in the specified level range): %s' % logging_stmt.strip())
278 return False
279
280 def disable_logging(lines, min_level_value, max_level_value):
281 """Disables logging statements in these lines whose logging level falls
282 between the specified minimum and maximum levels."""
283 output = ''
284 while lines:
285 line = lines[0]
286 ret = RE_LOGGING_START.match(line)
287 if not ret:
288 # no logging statement here, so just leave the line as-is and keep going
289 output += line
290 lines = lines[1:]
291 else:
292 # a logging call has started: find all the lines it includes and those it does not
293 logging_lines, remaining_lines = split_call(lines)
294 lines = remaining_lines
295 logging_stmt = ''.join(logging_lines)
296
297 # replace the logging statement if its level falls b/w min and max
298 if not check_level(logging_stmt, False, min_level_value, max_level_value):
299 output += logging_stmt
300 else:
301 # comment out this logging statement and replace it with pass
302 prefix_ws = ret.group(1)
303 pass_stmt = prefix_ws + PASS_LINE_CONTENTS
304 commented_out_logging_lines = comment_lines(logging_lines)
305 new_lines = pass_stmt + commented_out_logging_lines
306 logging.info('replacing:\n%s\nwith this:\n%s' % (logging_stmt.rstrip(), new_lines.rstrip()))
307 output += new_lines
308 return output
309
310 def restore_logging(lines, min_level_value, max_level_value):
311 """Re-enables logging statements in these lines whose logging level falls
312 between the specified minimum and maximum levels and which were disabled
313 by disable_logging() before."""
314 output = ''
315 while lines:
316 line = lines[0]
317 if line.lstrip() != PASS_LINE_CONTENTS:
318 # not our pass statement here, so just leave the line as-is and keep going
319 output += line
320 lines = lines[1:]
321 else:
322 # a logging call will start on the next line: find all the lines it includes and those it does not
323 logging_lines, remaining_lines = split_call(lines[1:])
324 lines = remaining_lines
325 logging_stmt = ''.join(logging_lines)
326 original_lines = line + logging_stmt
327
328 # replace the logging statement if its level falls b/w min and max
329 if not check_level(logging_stmt, True, min_level_value, max_level_value):
330 output += logging_stmt
331 else:
332 # uncomment_lines of this logging statement and remove the pass line
333 uncommented_logging_lines = uncomment_lines(logging_lines)
334 logging.info('replacing:\n%s\nwith this:\n%s' % (original_lines.rstrip(), uncommented_logging_lines.rstrip()))
335 output += uncommented_logging_lines
336 return output
337
338 if __name__ == "__main__":
339 logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.WARN)
340 sys.exit(main())
0 #!/bin/sh -e
1
2 ./scripts/disable_log_statements.sh
3 python setup.py sdist upload -r pypi
4 ./scripts/enable_log_statements.sh
3434 install_requires.append('greenlet')
3535
3636 setup(name='pynvim',
37 version='0.4.2',
37 version='0.4.3',
3838 description='Python client to neovim',
3939 url='http://github.com/neovim/pynvim',
40 download_url='https://github.com/neovim/pynvim/archive/0.4.2.tar.gz',
40 download_url='https://github.com/neovim/pynvim/archive/0.4.3.tar.gz',
4141 author='Thiago de Arruda',
4242 author_email='tpadilha84@gmail.com',
4343 license='Apache',
0 [tox]
1 envlist =
2 py{27,34,35,36,37,38}-{asyncio,pyuv}-cov,pypy-cov
3 checkqa
4
5 [testenv]
6 extras = test
7 deps =
8 pytest-timeout
9 cov: pytest-cov
10 pyuv: pyuv
11 setenv =
12 cov: PYTEST_ADDOPTS=--cov=. {env:PYTEST_ADDOPTS:}
13 passenv = PYTEST_ADDOPTS
14 commands =
15 python -m pytest {posargs}
16
17 [testenv:checkqa]
18 deps =
19 flake8
20 flake8-import-order
21 flake8-docstrings
22 pep8-naming
23 commands = flake8 {posargs:pynvim test}
24
25 [testenv:docs]
26 deps =
27 Sphinx
28 sphinx_rtd_theme
29 changedir = {toxinidir}/docs
30 commands =
31 sphinx-build -b html -d {envtmpdir}/doctrees . {envtmpdir}/html