Hello community,

here is the log from the commit of package python-neovim for openSUSE:Factory 
checked in at 2020-02-21 16:43:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-neovim (Old)
 and      /work/SRC/openSUSE:Factory/.python-neovim.new.26092 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-neovim"

Fri Feb 21 16:43:12 2020 rev:14 rq:777951 version:0.4.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-neovim/python-neovim.changes      
2019-01-24 14:12:28.755427260 +0100
+++ /work/SRC/openSUSE:Factory/.python-neovim.new.26092/python-neovim.changes   
2020-02-21 16:43:28.310166577 +0100
@@ -1,0 +2,25 @@
+Fri Feb 21 13:23:49 CET 2020 - Matej Cepl <[email protected]>
+
+- Update to 0.4.1:
+  - Logging will be disabled on release tarballs and pip packages
+    for performance reasons. use scripts/enable_log_statements.sh
+    and scripts/disable_log_statements.sh to toggle the
+    availability of logging.
+  - 09bba08 remove scrutinizer
+  - f048531 make pytest_runner an optional dependency
+  - 5b50ce9 fix missing self.name for nvim_error_event
+  - 175a2cc Test with python 3.8
+  - 5a2b552 fix the disable logging script.
+  - 2a31195 Update docs/tests to use --headless when needed
+  - 1d121e0 Update tests for new global/local option behavior
+  - 6310063 session: set client info (not only for host)
+  - 58ff62f python2 compat: fix buffer inequality
+  - a63cddb ci: fix coverage reporting
+  - f4f3bf5 api: key deletion; use KeyError for maps (if_python
+    compat)
+  - d3c389f host: do not run __init__ in plugin until the plugin
+    is invoked
+- Add temporary patch setup_version.patch setting the version
+  number in setup.py correctly. gh#neovim/pynvim#431
+
+-------------------------------------------------------------------

Old:
----
  pynvim-0.3.2.tar.gz

New:
----
  pynvim-0.4.1.tar.gz
  setup_version.patch

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-neovim.spec ++++++
--- /var/tmp/diff_new_pack.LzIbkw/_old  2020-02-21 16:43:29.266168487 +0100
+++ /var/tmp/diff_new_pack.LzIbkw/_new  2020-02-21 16:43:29.274168503 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-neovim
 #
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -20,13 +20,16 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 
 Name:           python-neovim
-Version:        0.3.2
+Version:        0.4.1
 Release:        0
 Summary:        Python client to Neovim
 License:        Apache-2.0
 Group:          Productivity/Text/Editors
 URL:            https://github.com/neovim/pynvim
-Source:         
https://github.com/neovim/pynvim/archive/%{version}/pynvim-%{version}.tar.gz
+Source:         
https://github.com/neovim/%{modname}/archive/%{version}/%{modname}-%{version}.tar.gz
+# PATCH-FIX-UPSTREAM setup_version.patch gh#neovim/pynvim#431 [email protected]
+# Upstream setup.py has incorrect version.
+Patch0:         setup_version.patch
 BuildRequires:  fdupes
 %if 0%{?rhel} >= 7
 BuildRequires:  python34-pytest

++++++ pynvim-0.3.2.tar.gz -> pynvim-0.4.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/.coveragerc new/pynvim-0.4.1/.coveragerc
--- old/pynvim-0.3.2/.coveragerc        1970-01-01 01:00:00.000000000 +0100
+++ new/pynvim-0.4.1/.coveragerc        2020-01-25 10:16:18.000000000 +0100
@@ -0,0 +1,8 @@
+[run]
+source = .
+branch = true
+parallel = 1
+
+[report]
+show_missing = true
+include = pynvim/*,test/*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/.scrutinizer.yml 
new/pynvim-0.4.1/.scrutinizer.yml
--- old/pynvim-0.3.2/.scrutinizer.yml   2019-01-20 16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/.scrutinizer.yml   1970-01-01 01:00:00.000000000 +0100
@@ -1,134 +0,0 @@
-checks:
-  python:
-    imports_relative_import: true
-    imports_wildcard_import: true
-    classes_no_self_argument: true
-    classes_bad_mcs_method_argument: true
-    classes_bad_classmethod_argument: true
-    code_rating: true
-    duplicate_code: true
-    variables_unused_variable: true
-    variables_unused_import: true
-    variables_unused_wildcard_import: true
-    variables_unused_argument: true
-    variables_global_variable_not_assigned: true
-    typecheck_redundant_keyword_arg: true
-    imports_import_self: true
-    format_superfluous_parens: true
-    exceptions_pointless_except: true
-    design_interface_not_implemented: true
-    design_abstract_class_not_used: true
-    basic_useless_else_on_loop: true
-    basic_unreachable: true
-    basic_unnecessary_pass: true
-    basic_unnecessary_lambda: true
-    basic_pointless_string_statement: true
-    basic_pointless_statement: true
-    basic_expression_not_assigned: true
-    variables_redefined_outer_name: true
-    variables_redefined_builtin: true
-    variables_redefine_in_handler: true
-    newstyle_bad_super_call: true
-    logging_not_lazy: true
-    exceptions_broad_except: true
-    exceptions_bare_except: true
-    classes_super_init_not_called: true
-    classes_protected_access: true
-    classes_non_parent_init_called: true
-    classes_bad_mcs_classmethod_argument: true
-    classes_attribute_defined_outside_init: true
-    classes_method_hidden: true
-    basic_lost_exception: true
-    basic_function_redefined: true
-    basic_exec_used: true
-    basic_eval_used: true
-    basic_dangerous_default_value: true
-    design_abstract_class_little_used: true
-    imports_deprecated_module: true
-    format_old_ne_operator: true
-    format_backtick: true
-    basic_old_raise_syntax: true
-    variables_used_before_assignment: true
-    variables_unpacking_non_sequence: true
-    variables_undefined_variable: true
-    variables_undefined_loop_variable: true
-    variables_undefined_all_variable: true
-    variables_unbalanced_tuple_unpacking: true
-    variables_no_name_in_module: true
-    variables_invalid_all_object: true
-    variables_global_variable_undefined: true
-    typecheck_unexpected_keyword_arg: true
-    typecheck_not_callable: true
-    typecheck_no_value_for_parameter: true
-    typecheck_no_member: true
-    typecheck_too_many_function_args: true
-    typecheck_missing_kwoa: true
-    typecheck_maybe_no_member: true
-    typecheck_duplicate_keyword_arg: true
-    typecheck_assignment_from_none: true
-    typecheck_assignment_from_no_return: true
-    string_unused_format_string_key: true
-    string_truncated_format_string: true
-    string_too_many_format_args: true
-    string_too_few_format_args: true
-    string_mixed_format_string: true
-    string_missing_format_string_key: true
-    string_format_needs_mapping: true
-    string_constant_anomalous_unicode_escape_in_string: true
-    string_constant_anomalous_backslash_in_string: true
-    string_bad_str_strip_call: true
-    string_bad_format_string_key: true
-    string_bad_format_character: true
-    open_mode_bad_open_mode: true
-    logging_unsupported_format: true
-    logging_too_many_args: true
-    logging_too_few_args: true
-    logging_format_truncated: true
-    imports_reimported: true
-    imports_import_error: true
-    imports_cyclic_import: true
-    exceptions_raising_string: true
-    exceptions_raising_non_exception: true
-    exceptions_raising_bad_type: true
-    exceptions_notimplemented_raised: true
-    exceptions_catching_non_exception: true
-    exceptions_bad_except_order: true
-    classes_valid_slots: true
-    classes_signature_differs: true
-    classes_non_iterator_returned: true
-    classes_no_method_argument: true
-    classes_missing_interface_method: true
-    classes_interface_is_not_class: true
-    classes_bad_staticmethod_argument: true
-    classes_bad_context_manager: true
-    classes_arguments_differ: true
-    classes_access_member_before_definition: true
-    classes_abstract_method: true
-    basic_yield_outside_function: true
-    basic_return_outside_function: true
-    basic_return_in_init: true
-    basic_return_arg_in_generator: true
-    basic_not_in_loop: true
-    basic_nonexistent_operator: true
-    basic_missing_reversed_argument: true
-    basic_missing_module_attribute: true
-    basic_init_is_generator: true
-    basic_duplicate_key: true
-    basic_duplicate_argument_name: true
-    basic_bad_reversed_sequence: true
-    basic_assert_on_tuple: true
-    basic_abstract_class_instantiated: true
-    format_lowercase_l_suffix: true
-    classes_no_self_use: true
-    classes_no_init: true
-    exceptions_binary_op_exception: true
-    variables_global_statement: true
-
-filter:
-  excluded_paths:
-    - test/*
-
-tools:
-  external_code_coverage:
-    timeout: 1200
-    runs: 6
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/.travis.yml new/pynvim-0.4.1/.travis.yml
--- old/pynvim-0.3.2/.travis.yml        2019-01-20 16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/.travis.yml        2020-01-25 10:16:18.000000000 +0100
@@ -2,7 +2,7 @@
 language: python
 env:
   global:
-    - PYTEST_ADDOPTS=-vv
+    - PYTEST_ADDOPTS="-vv --cov-append"
   matrix:
     - CI_TARGET=tests
 matrix:
@@ -11,27 +11,30 @@
       dist: trusty
       sudo: false
     - python: 3.6
-      env: CI_TARGET=checkqa TOXENV=checkqa
+      env: CI_TARGET=checkqa TOXENV=checkqa,docs
 python:
-  # If the build matrix gets bigger, also update the number of runs
-  # at the bottom of .scrutinizer.yml.
   - 2.7
   - 3.4
   - 3.5
   - 3.6
   - 3.7
-before_install:
+  - 3.8
+install:
   - if [ $CI_TARGET = tests ]; then
       eval "$(curl -Ss 
https://raw.githubusercontent.com/neovim/bot-ci/master/scripts/travis-setup.sh) 
nightly-x64";
-      pip install -q scrutinizer-ocular tox-travis;
+      pip install -q tox-travis;
     else
       pip install -q tox;
     fi
-install:
-  - pip install .
 script:
   - tox
 after_script:
   - if [ $CI_TARGET = tests ]; then
-      ocular;
+      set -x;
+      pip install coverage;
+      coverage combine;
+      coverage report -m;
+      coverage xml;
+      bash <(curl --retry 5 --silent --fail https://codecov.io/bash) -f 
coverage.xml;
+      set +x;
     fi
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/MANIFEST.in new/pynvim-0.4.1/MANIFEST.in
--- old/pynvim-0.3.2/MANIFEST.in        2019-01-20 16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/MANIFEST.in        2020-01-25 10:16:18.000000000 +0100
@@ -1 +1,2 @@
 include README.md LICENSE
+recursive-include test *.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/README.md new/pynvim-0.4.1/README.md
--- old/pynvim-0.3.2/README.md  2019-01-20 16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/README.md  2020-01-25 10:16:18.000000000 +0100
@@ -2,8 +2,7 @@
 
 [![Build 
Status](https://travis-ci.org/neovim/pynvim.svg?branch=master)](https://travis-ci.org/neovim/pynvim)
 [![Documentation 
Status](https://readthedocs.org/projects/pynvim/badge/?version=latest)](http://pynvim.readthedocs.io/en/latest/?badge=latest)
-[![Scrutinizer Code 
Quality](https://scrutinizer-ci.com/g/neovim/pynvim/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/neovim/pynvim/?branch=master)
-[![Code 
Coverage](https://scrutinizer-ci.com/g/neovim/pynvim/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/neovim/pynvim/?branch=master)
+[![Code 
coverage](https://codecov.io/gh/neovim/pynvim/branch/master/graph/badge.svg)](https://codecov.io/gh/neovim/pynvim)
 
 Pynvim implements support for python plugins in Nvim. It also works as a 
library for
 connecting to and scripting Nvim processes through its msgpack-rpc API.
@@ -103,12 +102,17 @@
 [1, 2, 3]
 ```
 
-You can embed neovim into your python application instead of binding to a
-running neovim instance.
+You can embed Neovim into your python application instead of connecting to
+a running Neovim instance.
 
 ```python
 >>> from pynvim import attach
->>> nvim = attach('child', argv=["/bin/env", "nvim", "--embed"])
+>>> nvim = attach('child', argv=["/bin/env", "nvim", "--embed", "--headless"])
 ```
 
-The tests can be consulted for more examples.
+- The ` --headless` argument tells `nvim` not to wait for a UI to connect.
+- Alternatively, use `--embed` _without_ `--headless` if your client is a UI
+  and you want `nvim` to wait for your client to `nvim_ui_attach` before
+  continuing startup.
+
+See the tests for more examples.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/codecov.yml new/pynvim-0.4.1/codecov.yml
--- old/pynvim-0.3.2/codecov.yml        1970-01-01 01:00:00.000000000 +0100
+++ new/pynvim-0.4.1/codecov.yml        2020-01-25 10:16:18.000000000 +0100
@@ -0,0 +1,6 @@
+coverage:
+  status:
+    project: true
+    patch: true
+    changes: true
+comment: false
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/docs/development.rst 
new/pynvim-0.4.1/docs/development.rst
--- old/pynvim-0.3.2/docs/development.rst       2019-01-20 16:22:43.000000000 
+0100
+++ new/pynvim-0.4.1/docs/development.rst       2020-01-25 10:16:18.000000000 
+0100
@@ -26,7 +26,7 @@
 
 If you want to test a different version than ``nvim`` in ``$PATH`` use::
 
-    NVIM_CHILD_ARGV='["/path/to/nvim", "-u", "NONE", "--embed"]' pytest
+    NVIM_CHILD_ARGV='["/path/to/nvim", "-u", "NONE", "--embed", "--headless"]' 
pytest
 
 Alternatively, if you want to see the state of nvim, you could use::
 
@@ -86,6 +86,6 @@
 .. code-block:: python
 
     >>> from pynvim import attach
-    >>> nvim = attach('child', argv=["/bin/env", "nvim", "--embed"])
+    >>> nvim = attach('child', argv=["/bin/env", "nvim", "--embed", 
"--headless"])
 
 The tests can be consulted for more examples.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/docs/usage/remote-plugins.rst 
new/pynvim-0.4.1/docs/usage/remote-plugins.rst
--- old/pynvim-0.3.2/docs/usage/remote-plugins.rst      2019-01-20 
16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/docs/usage/remote-plugins.rst      2020-01-25 
10:16:18.000000000 +0100
@@ -46,12 +46,15 @@
 
 .. note::
 
-    Plugins must not invoke API methods in ``__init__`` or global module scope
-    (or really do anything with non-trivial side-effects). A well-behaved 
rplugin
-    will not start executing until its functionality is requested by the user.
-    Initialize the plugin the first time the user invokes a command, or use an
-    appropriate autocommand, if it e.g. makes sense to automatically start the
-    plugin for a given filetype.
+    Plugin objects are constructed the first time any request of the class is
+    invoked. Any error in ``__init__`` will be reported as an error from this
+    first request. A well-behaved rplugin will not start executing until its
+    functionality is requested by the user. Initialize the plugin when user
+    invokes a command, or use an appropriate autocommand, e.g. FileType if it
+    makes sense to automatically start the plugin for a given filetype. Plugins
+    must not invoke API methods (or really do anything with non-trivial
+    side-effects) in global module scope, as the module might be loaded as part
+    of executing `UpdateRemotePlugins`.
 
 You need to run ``:UpdateRemotePlugins`` in Neovim for changes in the 
specifications to have effect.
 For details see ``:help remote-plugin`` in Neovim.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/pynvim/__init__.py 
new/pynvim-0.4.1/pynvim/__init__.py
--- old/pynvim-0.3.2/pynvim/__init__.py 2019-01-20 16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/pynvim/__init__.py 2020-01-25 10:16:18.000000000 +0100
@@ -135,12 +135,14 @@
             '%(filename)s:%(funcName)s:%(lineno)s] %(process)s - %(message)s')
         logging.root.addHandler(handler)
         level = logging.INFO
-        if 'NVIM_PYTHON_LOG_LEVEL' in os.environ:
-            lvl = getattr(logging,
-                          os.environ['NVIM_PYTHON_LOG_LEVEL'].strip(),
-                          level)
+        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)
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/pynvim/api/buffer.py 
new/pynvim-0.4.1/pynvim/api/buffer.py
--- old/pynvim-0.3.2/pynvim/api/buffer.py       2019-01-20 16:22:43.000000000 
+0100
+++ new/pynvim-0.4.1/pynvim/api/buffer.py       2020-01-25 10:16:18.000000000 
+0100
@@ -83,6 +83,13 @@
         """
         self.__setitem__(idx, None)
 
+    def __ne__(self, other):
+        """Test inequality of Buffers.
+
+        Necessary for Python 2 compatibility.
+        """
+        return not self.__eq__(other)
+
     def append(self, lines, index=-1):
         """Append a string or list of lines to the buffer."""
         if isinstance(lines, (basestring, bytes)):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/pynvim/api/common.py 
new/pynvim-0.4.1/pynvim/api/common.py
--- old/pynvim-0.3.2/pynvim/api/common.py       2019-01-20 16:22:43.000000000 
+0100
+++ new/pynvim-0.4.1/pynvim/api/common.py       2020-01-25 10:16:18.000000000 
+0100
@@ -5,6 +5,12 @@
 
 from ..compat import unicode_errors_default
 
+__all__ = ()
+
+
+class NvimError(Exception):
+    pass
+
 
 class Remote(object):
 
@@ -26,7 +32,8 @@
         self.handle = unpackb(code_data[1])
         self.api = RemoteApi(self, self._api_prefix)
         self.vars = RemoteMap(self, self._api_prefix + 'get_var',
-                              self._api_prefix + 'set_var')
+                              self._api_prefix + 'set_var',
+                              self._api_prefix + 'del_var')
         self.options = RemoteMap(self, self._api_prefix + 'get_option',
                                  self._api_prefix + 'set_option')
 
@@ -65,8 +72,16 @@
         return functools.partial(self._obj.request, self._api_prefix + name)
 
 
-class RemoteMap(object):
+def transform_keyerror(exc):
+    if isinstance(exc, NvimError):
+        if exc.args[0].startswith('Key not found:'):
+            return KeyError(exc.args[0])
+        if exc.args[0].startswith('Invalid option name:'):
+            return KeyError(exc.args[0])
+    return exc
 
+
+class RemoteMap(object):
     """Represents a string->object map stored in Nvim.
 
     This is the dict counterpart to the `RemoteSequence` class, but it is used
@@ -76,16 +91,23 @@
     It is used to provide a dict-like API to vim variables and options.
     """
 
-    def __init__(self, obj, get_method, set_method=None):
+    _set = None
+    _del = None
+
+    def __init__(self, obj, get_method, set_method=None, del_method=None):
         """Initialize a RemoteMap with session, getter/setter."""
         self._get = functools.partial(obj.request, get_method)
-        self._set = None
         if set_method:
             self._set = functools.partial(obj.request, set_method)
+        if del_method:
+            self._del = functools.partial(obj.request, del_method)
 
     def __getitem__(self, key):
         """Return a map value by key."""
-        return self._get(key)
+        try:
+            return self._get(key)
+        except NvimError as exc:
+            raise transform_keyerror(exc)
 
     def __setitem__(self, key, value):
         """Set a map value by key(if the setter was provided)."""
@@ -95,9 +117,12 @@
 
     def __delitem__(self, key):
         """Delete a map value by associating None with the key."""
-        if not self._set:
+        if not self._del:
             raise TypeError('This dict is read-only')
-        return self._set(key, None)
+        try:
+            return self._del(key)
+        except NvimError as exc:
+            raise transform_keyerror(exc)
 
     def __contains__(self, key):
         """Check if key is present in the map."""
@@ -110,8 +135,8 @@
     def get(self, key, default=None):
         """Return value for key if present, else a default value."""
         try:
-            return self._get(key)
-        except Exception:
+            return self.__getitem__(key)
+        except KeyError:
             return default
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/pynvim/api/nvim.py 
new/pynvim-0.4.1/pynvim/api/nvim.py
--- old/pynvim-0.3.2/pynvim/api/nvim.py 2019-01-20 16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/pynvim/api/nvim.py 2020-01-25 10:16:18.000000000 +0100
@@ -8,7 +8,7 @@
 from msgpack import ExtType
 
 from .buffer import Buffer
-from .common import (Remote, RemoteApi, RemoteMap, RemoteSequence,
+from .common import (NvimError, Remote, RemoteApi, RemoteMap, RemoteSequence,
                      decode_if_bytes, walk)
 from .tabpage import Tabpage
 from .window import Window
@@ -76,8 +76,8 @@
         queries Nvim metadata for type information and sets a SessionHook for
         creating specialized objects from Nvim remote handles.
         """
-        session.error_wrapper = lambda e: NvimError(e[1])
-        channel_id, metadata = session.request(b'vim_get_api_info')
+        session.error_wrapper = lambda e: NvimError(decode_if_bytes(e[1]))
+        channel_id, metadata = session.request(b'nvim_get_api_info')
 
         if IS_PYTHON3:
             # decode all metadata strings for python3
@@ -107,8 +107,8 @@
         self.version = Version(**version)
         self.types = types
         self.api = RemoteApi(self, 'nvim_')
-        self.vars = RemoteMap(self, 'nvim_get_var', 'nvim_set_var')
-        self.vvars = RemoteMap(self, 'nvim_get_vvar', None)
+        self.vars = RemoteMap(self, 'nvim_get_var', 'nvim_set_var', 
'nvim_del_var')
+        self.vvars = RemoteMap(self, 'nvim_get_vvar', None, None)
         self.options = RemoteMap(self, 'nvim_get_option', 'nvim_set_option')
         self.buffers = Buffers(self)
         self.windows = RemoteSequence(self, 'nvim_list_wins')
@@ -389,11 +389,17 @@
                             from_part, do_lt, special)
 
     def out_write(self, msg, **kwargs):
-        """Print `msg` as a normal message."""
+        r"""Print `msg` as a normal message.
+
+        The message is buffered (won't display) until linefeed ("\n").
+        """
         return self.request('nvim_out_write', msg, **kwargs)
 
     def err_write(self, msg, **kwargs):
-        """Print `msg` as an error message."""
+        r"""Print `msg` as an error message.
+
+        The message is buffered (won't display) until linefeed ("\n").
+        """
         if self._thread_invalid():
             # special case: if a non-main thread writes to stderr
             # i.e. due to an uncaught exception, pass it through
@@ -569,7 +575,3 @@
         pattern = "return {}(...)" if not async_ else "{}(...)"
         code = pattern.format(self.name)
         return self._nvim.exec_lua(code, *args, **kwargs)
-
-
-class NvimError(Exception):
-    pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/pynvim/msgpack_rpc/__init__.py 
new/pynvim-0.4.1/pynvim/msgpack_rpc/__init__.py
--- old/pynvim-0.3.2/pynvim/msgpack_rpc/__init__.py     2019-01-20 
16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/pynvim/msgpack_rpc/__init__.py     2020-01-25 
10:16:18.000000000 +0100
@@ -8,6 +8,7 @@
 from .event_loop import EventLoop
 from .msgpack_stream import MsgpackStream
 from .session import ErrorResponse, Session
+from ..util import get_client_info
 
 
 __all__ = ('tcp_session', 'socket_session', 'stdio_session', 'child_session',
@@ -19,6 +20,8 @@
     msgpack_stream = MsgpackStream(loop)
     async_session = AsyncSession(msgpack_stream)
     session = Session(async_session)
+    session.request(b'nvim_set_client_info',
+                    *get_client_info('client', 'remote', {}), async_=True)
     return session
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/pynvim/msgpack_rpc/async_session.py 
new/pynvim-0.4.1/pynvim/msgpack_rpc/async_session.py
--- old/pynvim-0.3.2/pynvim/msgpack_rpc/async_session.py        2019-01-20 
16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/pynvim/msgpack_rpc/async_session.py        2020-01-25 
10:16:18.000000000 +0100
@@ -57,7 +57,7 @@
     def run(self, request_cb, notification_cb):
         """Run the event loop to receive requests and notifications from Nvim.
 
-        While the event loop is running, `request_cb` and `_notification_cb`
+        While the event loop is running, `request_cb` and `notification_cb`
         will be called whenever requests or notifications are respectively
         available.
         """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/pynvim/msgpack_rpc/event_loop/base.py 
new/pynvim-0.4.1/pynvim/msgpack_rpc/event_loop/base.py
--- old/pynvim-0.3.2/pynvim/msgpack_rpc/event_loop/base.py      2019-01-20 
16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/pynvim/msgpack_rpc/event_loop/base.py      2020-01-25 
10:16:18.000000000 +0100
@@ -33,7 +33,7 @@
       named pipe.
     - `_connect_stdio()`: Use stdin/stdout as the connection to Nvim
     - `_connect_child(argv)`: Use the argument vector `argv` to spawn an
-      embedded Nvim that has it's stdin/stdout connected to the event loop.
+      embedded Nvim that has its stdin/stdout connected to the event loop.
     - `_start_reading()`: Called after any of _connect_* methods. Can be used
       to perform any post-connection setup or validation.
     - `_send(data)`: Send `data`(byte array) to Nvim. The data is only
@@ -70,7 +70,8 @@
         Traceback (most recent call last):
             ...
         AttributeError: 'BaseEventLoop' object has no attribute '_init'
-        >>> BaseEventLoop('child', ['nvim', '--embed', '-u', 'NONE'])
+        >>> BaseEventLoop('child',
+                ['nvim', '--embed', '--headless', '-u', 'NONE'])
         Traceback (most recent call last):
             ...
         AttributeError: 'BaseEventLoop' object has no attribute '_init'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/pynvim/plugin/host.py 
new/pynvim-0.4.1/pynvim/plugin/host.py
--- old/pynvim-0.3.2/pynvim/plugin/host.py      2019-01-20 16:22:43.000000000 
+0100
+++ new/pynvim-0.4.1/pynvim/plugin/host.py      2020-01-25 10:16:18.000000000 
+0100
@@ -5,7 +5,6 @@
 import os
 import os.path
 import re
-import sys
 from functools import partial
 from traceback import format_exc
 
@@ -13,7 +12,7 @@
 from ..api import decode_if_bytes, walk
 from ..compat import IS_PYTHON3, find_module
 from ..msgpack_rpc import ErrorResponse
-from ..util import VERSION, format_exc_skip
+from ..util import format_exc_skip, get_client_info
 
 __all__ = ('Host')
 
@@ -60,6 +59,7 @@
         errmsg = "{}: Async request caused an error:\n{}\n".format(
             self.name, decode_if_bytes(msg))
         self.nvim.err_write(errmsg, async_=True)
+        return errmsg
 
     def start(self, plugins):
         """Start listening for msgpack-rpc requests and notifications."""
@@ -73,6 +73,26 @@
         self._unload()
         self.nvim.stop_loop()
 
+    def _wrap_delayed_function(self, cls, delayed_handlers, name, sync,
+                               module_handlers, path, *args):
+        # delete the delayed handlers to be sure
+        for handler in delayed_handlers:
+            method_name = handler._nvim_registered_name
+            if handler._nvim_rpc_sync:
+                del self._request_handlers[method_name]
+            else:
+                del self._notification_handlers[method_name]
+        # create an instance of the plugin and pass the nvim object
+        plugin = cls(self._configure_nvim_for(cls))
+
+        # discover handlers in the plugin instance
+        self._discover_functions(plugin, module_handlers, path, False)
+
+        if sync:
+            self._request_handlers[name](*args)
+        else:
+            self._notification_handlers[name](*args)
+
     def _wrap_function(self, fn, sync, decode, nvim_bind, name, *args):
         if decode:
             args = walk(decode_if_bytes, args, decode)
@@ -145,7 +165,7 @@
                     module = imp.load_module(name, file, pathname, descr)
                 handlers = []
                 self._discover_classes(module, handlers, path)
-                self._discover_functions(module, handlers, path)
+                self._discover_functions(module, handlers, path, False)
                 if not handlers:
                     error('{} exports no handlers'.format(path))
                     continue
@@ -156,23 +176,17 @@
                 error(err)
                 self._load_errors[path] = err
 
-        if len(plugins) == 1 and has_script:
-            kind = "script"
-        else:
-            kind = "rplugin"
-        name = "python{}-{}-host".format(sys.version_info[0], kind)
-        attributes = {"license": "Apache v2",
-                      "website": "github.com/neovim/pynvim"}
-        self.name = name
-        self.nvim.api.set_client_info(
-            name, VERSION.__dict__, "host", host_method_spec,
-            attributes, async_=True)
+        kind = ("script-host" if len(plugins) == 1 and has_script
+                else "rplugin-host")
+        info = get_client_info(kind, 'host', host_method_spec)
+        self.name = info[0]
+        self.nvim.api.set_client_info(*info, async_=True)
 
     def _unload(self):
         for path, plugin in self._loaded.items():
             handlers = plugin['handlers']
             for handler in handlers:
-                method_name = handler._nvim_rpc_method_name
+                method_name = handler._nvim_registered_name
                 if hasattr(handler, '_nvim_shutdown_hook'):
                     handler()
                 elif handler._nvim_rpc_sync:
@@ -185,31 +199,35 @@
     def _discover_classes(self, module, handlers, plugin_path):
         for _, cls in inspect.getmembers(module, inspect.isclass):
             if getattr(cls, '_nvim_plugin', False):
-                # create an instance of the plugin and pass the nvim object
-                plugin = cls(self._configure_nvim_for(cls))
                 # discover handlers in the plugin instance
-                self._discover_functions(plugin, handlers, plugin_path)
+                self._discover_functions(cls, handlers, plugin_path, True)
 
-    def _discover_functions(self, obj, handlers, plugin_path):
+    def _discover_functions(self, obj, handlers, plugin_path, delay):
         def predicate(o):
             return hasattr(o, '_nvim_rpc_method_name')
 
+        cls_handlers = []
         specs = []
         objdecode = getattr(obj, '_nvim_decode', self._decode_default)
         for _, fn in inspect.getmembers(obj, predicate):
-            sync = fn._nvim_rpc_sync
-            decode = getattr(fn, '_nvim_decode', objdecode)
-            nvim_bind = None
-            if fn._nvim_bind:
-                nvim_bind = self._configure_nvim_for(fn)
-
             method = fn._nvim_rpc_method_name
             if fn._nvim_prefix_plugin_path:
                 method = '{}:{}'.format(plugin_path, method)
+            sync = fn._nvim_rpc_sync
+            if delay:
+                fn_wrapped = partial(self._wrap_delayed_function, obj,
+                                     cls_handlers, method, sync,
+                                     handlers, plugin_path)
+            else:
+                decode = getattr(fn, '_nvim_decode', objdecode)
+                nvim_bind = None
+                if fn._nvim_bind:
+                    nvim_bind = self._configure_nvim_for(fn)
 
-            fn_wrapped = partial(self._wrap_function, fn,
-                                 sync, decode, nvim_bind, method)
+                fn_wrapped = partial(self._wrap_function, fn,
+                                     sync, decode, nvim_bind, method)
             self._copy_attributes(fn, fn_wrapped)
+            fn_wrapped._nvim_registered_name = method
             # register in the rpc handler dict
             if sync:
                 if method in self._request_handlers:
@@ -224,6 +242,7 @@
             if hasattr(fn, '_nvim_rpc_spec'):
                 specs.append(fn._nvim_rpc_spec)
             handlers.append(fn_wrapped)
+            cls_handlers.append(fn_wrapped)
         if specs:
             self._specs[plugin_path] = specs
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/pynvim/util.py 
new/pynvim-0.4.1/pynvim/util.py
--- old/pynvim-0.3.2/pynvim/util.py     2019-01-20 16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/pynvim/util.py     2020-01-25 10:16:18.000000000 +0100
@@ -14,7 +14,6 @@
 
 # Taken from SimpleNamespace in python 3
 class Version:
-
     """Helper class for version info."""
 
     def __init__(self, **kwargs):
@@ -32,4 +31,12 @@
         return self.__dict__ == other.__dict__
 
 
-VERSION = Version(major=0, minor=3, patch=2, prerelease='')
+def get_client_info(kind, type_, method_spec):
+    """Returns a tuple describing the client."""
+    name = "python{}-{}".format(sys.version_info[0], kind)
+    attributes = {"license": "Apache v2",
+                  "website": "github.com/neovim/pynvim"}
+    return (name, VERSION.__dict__, type_, method_spec, attributes)
+
+
+VERSION = Version(major=0, minor=4, patch=1, prerelease='')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/scripts/disable_log_statements.sh 
new/pynvim-0.4.1/scripts/disable_log_statements.sh
--- old/pynvim-0.3.2/scripts/disable_log_statements.sh  2019-01-20 
16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/scripts/disable_log_statements.sh  2020-01-25 
10:16:18.000000000 +0100
@@ -1,4 +1,4 @@
 #!/bin/sh -e
 
-cd neovim
+cd pynvim
 find -name '*.py' | xargs -i{} ../scripts/logging_statement_modifier.py {}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/scripts/enable_log_statements.sh 
new/pynvim-0.4.1/scripts/enable_log_statements.sh
--- old/pynvim-0.3.2/scripts/enable_log_statements.sh   2019-01-20 
16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/scripts/enable_log_statements.sh   2020-01-25 
10:16:18.000000000 +0100
@@ -1,4 +1,4 @@
 #!/bin/sh -e
 
-cd neovim
+cd pynvim
 find -name '*.py' | xargs -i{} ../scripts/logging_statement_modifier.py 
--restore {}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/scripts/logging_statement_modifier.py 
new/pynvim-0.4.1/scripts/logging_statement_modifier.py
--- old/pynvim-0.3.2/scripts/logging_statement_modifier.py      2019-01-20 
16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/scripts/logging_statement_modifier.py      2020-01-25 
10:16:18.000000000 +0100
@@ -105,7 +105,7 @@
     min_level_value = 0 if options.min_level == 'NONE' else 
get_level_value(options.min_level)
     if options.min_level is None:
         parser.error("min level must be an integer or one of these values: %s" 
% ', '.join(LEVEL_CHOICES))
-    max_level_value = sys.maxint if options.max_level == 'NONE' else 
get_level_value(options.max_level)
+    max_level_value = 9000 if options.max_level == 'NONE' else 
get_level_value(options.max_level)
     if options.max_level is None:
         parser.error("max level must be an integer or one of these values: %s" 
% ', '.join(LEVEL_CHOICES))
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/setup.cfg new/pynvim-0.4.1/setup.cfg
--- old/pynvim-0.3.2/setup.cfg  2019-01-20 16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/setup.cfg  2020-01-25 10:16:18.000000000 +0100
@@ -1,5 +1,15 @@
+[aliases]
+test = pytest
+
 [flake8]
-ignore = D211,E731,D401,W503
+extend-ignore = D211,E731,D401,W503
+max-line-length = 88
+per-file-ignores =
+  test/*:D1
+application-import-names = pynvim
+
+[isort]
+known_first_party = pynvim
 
 [tool:pytest]
 testpaths = test
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/setup.py new/pynvim-0.4.1/setup.py
--- old/pynvim-0.3.2/setup.py   2019-01-20 16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/setup.py   2020-01-25 10:16:18.000000000 +0100
@@ -8,6 +8,12 @@
     'msgpack>=0.5.0',
 ]
 
+needs_pytest = {'pytest', 'test', 'ptr'}.intersection(sys.argv)
+pytest_runner = ['pytest-runner'] if needs_pytest else []
+
+setup_requires = [
+] + pytest_runner,
+
 tests_require = [
     'pytest>=3.4.0',
 ]
@@ -29,10 +35,10 @@
     install_requires.append('greenlet')
 
 setup(name='pynvim',
-      version='0.3.2',
+      version='0.4.0',
       description='Python client to neovim',
-      url='http://github.com/neovim/python-client',
-      
download_url='https://github.com/neovim/python-client/archive/0.3.2.tar.gz',
+      url='http://github.com/neovim/pynvim',
+      download_url='https://github.com/neovim/pynvim/archive/0.4.1.tar.gz',
       author='Thiago de Arruda',
       author_email='[email protected]',
       license='Apache',
@@ -40,6 +46,7 @@
                 'pynvim.msgpack_rpc.event_loop', 'pynvim.plugin',
                 'neovim', 'neovim.api'],
       install_requires=install_requires,
+      setup_requires=setup_requires,
       tests_require=tests_require,
       extras_require=extras_require,
       zip_safe=False)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/test/conftest.py 
new/pynvim-0.4.1/test/conftest.py
--- old/pynvim-0.3.2/test/conftest.py   2019-01-20 16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/test/conftest.py   2020-01-25 10:16:18.000000000 +0100
@@ -1,55 +1,11 @@
 import json
 import os
-import textwrap
 
-import pynvim
 import pytest
 
-pynvim.setup_logging("test")
-
+import pynvim
 
[email protected](autouse=True)
-def cleanup_func(vim):
-    fun = textwrap.dedent('''function! BeforeEachTest()
-        set all&
-        redir => groups
-        silent augroup
-        redir END
-        for group in split(groups)
-            exe 'augroup '.group
-            autocmd!
-            augroup END
-        endfor
-        autocmd!
-        tabnew
-        let curbufnum = eval(bufnr('%'))
-        redir => buflist
-        silent ls!
-        redir END
-        let bufnums = []
-        for buf in split(buflist, '\\n')
-            let bufnum = eval(split(buf, '[ u]')[0])
-            if bufnum != curbufnum
-            call add(bufnums, bufnum)
-            endif
-        endfor
-        if len(bufnums) > 0
-            exe 'silent bwipeout! '.join(bufnums, ' ')
-        endif
-        silent tabonly
-        for k in keys(g:)
-            exe 'unlet g:'.k
-        endfor
-        filetype plugin indent off
-        mapclear
-        mapclear!
-        abclear
-        comclear
-        endfunction
-    ''')
-    vim.command(fun)
-    vim.command('call BeforeEachTest()')
-    assert len(vim.tabpages) == len(vim.windows) == len(vim.buffers) == 1
+pynvim.setup_logging("test")
 
 
 @pytest.fixture
@@ -57,11 +13,12 @@
     child_argv = os.environ.get('NVIM_CHILD_ARGV')
     listen_address = os.environ.get('NVIM_LISTEN_ADDRESS')
     if child_argv is None and listen_address is None:
-        child_argv = '["nvim", "-u", "NONE", "--embed"]'
+        child_argv = '["nvim", "-u", "NONE", "--embed", "--headless"]'
 
     if child_argv is not None:
         editor = pynvim.attach('child', argv=json.loads(child_argv))
     else:
+        assert listen_address is None or listen_address != ''
         editor = pynvim.attach('socket', path=listen_address)
 
     return editor
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/test/test_buffer.py 
new/pynvim-0.4.1/test/test_buffer.py
--- old/pynvim-0.3.2/test/test_buffer.py        2019-01-20 16:22:43.000000000 
+0100
+++ new/pynvim-0.4.1/test/test_buffer.py        2020-01-25 10:16:18.000000000 
+0100
@@ -1,10 +1,13 @@
 import os
 
+import pytest
+
+from pynvim.api import NvimError
 from pynvim.compat import IS_PYTHON3
 
 
 def test_repr(vim):
-    assert repr(vim.current.buffer) == "<Buffer(handle=2)>"
+    assert repr(vim.current.buffer) == "<Buffer(handle=1)>"
 
 
 def test_get_length(vim):
@@ -74,6 +77,17 @@
     vim.current.buffer.vars['python'] = [1, 2, {'3': 1}]
     assert vim.current.buffer.vars['python'] == [1, 2, {'3': 1}]
     assert vim.eval('b:python') == [1, 2, {'3': 1}]
+    assert vim.current.buffer.vars.get('python') == [1, 2, {'3': 1}]
+
+    del vim.current.buffer.vars['python']
+    with pytest.raises(KeyError):
+        vim.current.buffer.vars['python']
+    assert vim.eval('exists("b:python")') == 0
+
+    with pytest.raises(KeyError):
+        del vim.current.buffer.vars['python']
+
+    assert vim.current.buffer.vars.get('python', 'default') == 'default'
 
 
 def test_api(vim):
@@ -93,7 +107,11 @@
     vim.current.buffer.options['define'] = 'test'
     assert vim.current.buffer.options['define'] == 'test'
     # Doesn't change the global value
-    assert vim.options['define'] == '^\s*#\s*define'
+    assert vim.options['define'] == r'^\s*#\s*define'
+
+    with pytest.raises(KeyError) as excinfo:
+        vim.current.buffer.options['doesnotexist']
+    assert excinfo.value.args == ("Invalid option name: 'doesnotexist'",)
 
 
 def test_number(vim):
@@ -154,21 +172,30 @@
 
 
 def test_get_exceptions(vim):
-    try:
+    with pytest.raises(KeyError) as excinfo:
         vim.current.buffer.options['invalid-option']
-        assert False
-    except vim.error:
-        pass
+
+    assert not isinstance(excinfo.value, NvimError)
+    assert excinfo.value.args == ("Invalid option name: 'invalid-option'",)
+
 
 def test_set_items_for_range(vim):
     vim.current.buffer[:] = ['a', 'b', 'c', 'd', 'e']
     r = vim.current.buffer.range(1, 3)
-    r[1:3] = ['foo']*3
+    r[1:3] = ['foo'] * 3
     assert vim.current.buffer[:] == ['a', 'foo', 'foo', 'foo', 'd', 'e']
 
+
 # NB: we can't easily test the effect of this. But at least run the lua
 # function sync, so we know it runs without runtime error with simple args.
 def test_update_highlights(vim):
     vim.current.buffer[:] = ['a', 'b', 'c']
     src_id = vim.new_highlight_source()
-    vim.current.buffer.update_highlights(src_id, [["Comment", 0, 0, -1], 
("String", 1, 0, 1)], clear=True, async_=False)
+    vim.current.buffer.update_highlights(
+        src_id, [["Comment", 0, 0, -1], ("String", 1, 0, 1)], clear=True, 
async_=False
+    )
+
+
+def test_buffer_inequality(vim):
+    b = vim.current.buffer
+    assert not (b != vim.current.buffer)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/test/test_client_rpc.py 
new/pynvim-0.4.1/test/test_client_rpc.py
--- old/pynvim-0.3.2/test/test_client_rpc.py    2019-01-20 16:22:43.000000000 
+0100
+++ new/pynvim-0.4.1/test/test_client_rpc.py    2020-01-25 10:16:18.000000000 
+0100
@@ -4,6 +4,7 @@
 
 def test_call_and_reply(vim):
     cid = vim.channel_id
+
     def setup_cb():
         cmd = 'let g:result = rpcrequest(%d, "client-call", 1, 2, 3)' % cid
         vim.command(cmd)
@@ -20,6 +21,7 @@
 
 def test_call_api_before_reply(vim):
     cid = vim.channel_id
+
     def setup_cb():
         cmd = 'let g:result = rpcrequest(%d, "client-call2", 1, 2, 3)' % cid
         vim.command(cmd)
@@ -32,6 +34,7 @@
 
     vim.run_loop(request_cb, None, setup_cb)
 
+
 def test_async_call(vim):
 
     def request_cb(name, args):
@@ -52,6 +55,7 @@
 
 def test_recursion(vim):
     cid = vim.channel_id
+
     def setup_cb():
         vim.vars['result1'] = 0
         vim.vars['result2'] = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/test/test_concurrency.py 
new/pynvim-0.4.1/test/test_concurrency.py
--- old/pynvim-0.3.2/test/test_concurrency.py   2019-01-20 16:22:43.000000000 
+0100
+++ new/pynvim-0.4.1/test/test_concurrency.py   2020-01-25 10:16:18.000000000 
+0100
@@ -4,13 +4,13 @@
 def test_interrupt_from_another_thread(vim):
     timer = Timer(0.5, lambda: vim.async_call(lambda: vim.stop_loop()))
     timer.start()
-    assert vim.next_message() == None
+    assert vim.next_message() is None
 
 
 def test_exception_in_threadsafe_call(vim):
     # an exception in a threadsafe_call shouldn't crash the entire host
     msgs = []
-    vim.async_call(lambda: [vim.eval("3"), undefined_variable])
+    vim.async_call(lambda: [vim.eval("3"), undefined_variable])  # noqa: F821
     timer = Timer(0.5, lambda: vim.async_call(lambda: vim.stop_loop()))
     timer.start()
     vim.run_loop(None, None, err_cb=msgs.append)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/test/test_decorators.py 
new/pynvim-0.4.1/test/test_decorators.py
--- old/pynvim-0.3.2/test/test_decorators.py    2019-01-20 16:22:43.000000000 
+0100
+++ new/pynvim-0.4.1/test/test_decorators.py    2020-01-25 10:16:18.000000000 
+0100
@@ -3,7 +3,7 @@
 
 def test_command_count():
     def function():
-        "A dummy function to decorate."
+        """A dummy function to decorate."""
         return
 
     # ensure absence with default value of None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/test/test_events.py 
new/pynvim-0.4.1/test/test_events.py
--- old/pynvim-0.3.2/test/test_events.py        2019-01-20 16:22:43.000000000 
+0100
+++ new/pynvim-0.4.1/test/test_events.py        2020-01-25 10:16:18.000000000 
+0100
@@ -28,6 +28,13 @@
     assert vim.eval('g:data') == 'xyz'
 
 
+def test_async_error(vim):
+    # Invoke a bogus Ex command via notify (async).
+    vim.command("lolwut", async_=True)
+    event = vim.next_message()
+    assert event[1] == 'nvim_error_event'
+
+
 def test_broadcast(vim):
     vim.subscribe('event2')
     vim.command('call rpcnotify(0, "event1", 1, 2, 3)')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/test/test_host.py 
new/pynvim-0.4.1/test/test_host.py
--- old/pynvim-0.3.2/test/test_host.py  2019-01-20 16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/test/test_host.py  2020-01-25 10:16:18.000000000 +0100
@@ -2,6 +2,22 @@
 
 from pynvim.plugin.host import Host, host_method_spec
 
-def test_host_method_spec(vim):
+
+def test_host_clientinfo(vim):
     h = Host(vim)
     assert h._request_handlers.keys() == host_method_spec.keys()
+    assert 'remote' == vim.api.get_chan_info(vim.channel_id)['client']['type']
+    h._load([])
+    assert 'host' == vim.api.get_chan_info(vim.channel_id)['client']['type']
+
+
+# Smoke test for Host._on_error_event(). #425
+def test_host_async_error(vim):
+    h = Host(vim)
+    h._load([])
+    # Invoke a bogus Ex command via notify (async).
+    vim.command("lolwut", async_=True)
+    event = vim.next_message()
+    assert event[1] == 'nvim_error_event'
+    assert 'rplugin-host: Async request caused an error:\nboom\n' \
+           in h._on_error_event(None, 'boom')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/test/test_logging.py 
new/pynvim-0.4.1/test/test_logging.py
--- old/pynvim-0.3.2/test/test_logging.py       1970-01-01 01:00:00.000000000 
+0100
+++ new/pynvim-0.4.1/test/test_logging.py       2020-01-25 10:16:18.000000000 
+0100
@@ -0,0 +1,36 @@
+import os
+import sys
+
+
+def test_setup_logging(monkeypatch, tmpdir, caplog):
+    from pynvim import setup_logging
+
+    major_version = sys.version_info[0]
+
+    setup_logging('name1')
+    assert caplog.messages == []
+
+    def get_expected_logfile(prefix, name):
+        return '{}_py{}_{}'.format(prefix, major_version, name)
+
+    prefix = tmpdir.join('testlog1')
+    monkeypatch.setenv('NVIM_PYTHON_LOG_FILE', str(prefix))
+    setup_logging('name2')
+    assert caplog.messages == []
+    logfile = get_expected_logfile(prefix, 'name2')
+    assert os.path.exists(logfile)
+    assert open(logfile, 'r').read() == ''
+
+    monkeypatch.setenv('NVIM_PYTHON_LOG_LEVEL', 'invalid')
+    setup_logging('name3')
+    assert caplog.record_tuples == [
+        ('pynvim', 30, "Invalid NVIM_PYTHON_LOG_LEVEL: 'invalid', using 
INFO."),
+    ]
+    logfile = get_expected_logfile(prefix, 'name2')
+    assert os.path.exists(logfile)
+    with open(logfile, 'r') as f:
+        lines = f.readlines()
+        assert len(lines) == 1
+        assert lines[0].endswith(
+            "- Invalid NVIM_PYTHON_LOG_LEVEL: 'invalid', using INFO.\n"
+        )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/test/test_tabpage.py 
new/pynvim-0.4.1/test/test_tabpage.py
--- old/pynvim-0.3.2/test/test_tabpage.py       2019-01-20 16:22:43.000000000 
+0100
+++ new/pynvim-0.4.1/test/test_tabpage.py       2020-01-25 10:16:18.000000000 
+0100
@@ -1,3 +1,6 @@
+import pytest
+
+
 def test_windows(vim):
     vim.command('tabnew')
     vim.command('vsplit')
@@ -12,6 +15,17 @@
     vim.current.tabpage.vars['python'] = [1, 2, {'3': 1}]
     assert vim.current.tabpage.vars['python'] == [1, 2, {'3': 1}]
     assert vim.eval('t:python') == [1, 2, {'3': 1}]
+    assert vim.current.tabpage.vars.get('python') == [1, 2, {'3': 1}]
+
+    del vim.current.tabpage.vars['python']
+    with pytest.raises(KeyError):
+        vim.current.tabpage.vars['python']
+    assert vim.eval('exists("t:python")') == 0
+
+    with pytest.raises(KeyError):
+        del vim.current.tabpage.vars['python']
+
+    assert vim.current.tabpage.vars.get('python', 'default') == 'default'
 
 
 def test_valid(vim):
@@ -31,4 +45,4 @@
 
 
 def test_repr(vim):
-    assert repr(vim.current.tabpage) == "<Tabpage(handle=2)>"
+    assert repr(vim.current.tabpage) == "<Tabpage(handle=1)>"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/test/test_vim.py 
new/pynvim-0.4.1/test/test_vim.py
--- old/pynvim-0.3.2/test/test_vim.py   2019-01-20 16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/test/test_vim.py   2020-01-25 10:16:18.000000000 +0100
@@ -3,6 +3,8 @@
 import sys
 import tempfile
 
+import pytest
+
 
 def source(vim, code):
     fd, fname = tempfile.mkstemp()
@@ -12,6 +14,10 @@
     os.unlink(fname)
 
 
+def test_clientinfo(vim):
+    assert 'remote' == vim.api.get_chan_info(vim.channel_id)['client']['type']
+
+
 def test_command(vim):
     fname = tempfile.mkstemp()[1]
     vim.command('new')
@@ -30,14 +36,22 @@
     assert vim.command_output('echo "test"') == 'test'
 
 
+def test_command_error(vim):
+    with pytest.raises(vim.error) as excinfo:
+        vim.current.window.cursor = -1, -1
+    assert excinfo.value.args == ('Cursor position outside buffer',)
+
+
 def test_eval(vim):
     vim.command('let g:v1 = "a"')
     vim.command('let g:v2 = [1, 2, {"v3": 3}]')
-    assert vim.eval('g:'), {'v1': 'a', 'v2': [1, 2 == {'v3': 3}]}
+    g = vim.eval('g:')
+    assert g['v1'] == 'a'
+    assert g['v2'] == [1, 2, {'v3': 3}]
 
 
 def test_call(vim):
-    assert vim.funcs.join(['first', 'last'], ', '), 'first == last'
+    assert vim.funcs.join(['first', 'last'], ', ') == 'first, last'
     source(vim, """
         function! Testfun(a,b)
             return string(a:a).":".a:b
@@ -87,12 +101,29 @@
     vim.vars['python'] = [1, 2, {'3': 1}]
     assert vim.vars['python'], [1, 2 == {'3': 1}]
     assert vim.eval('g:python'), [1, 2 == {'3': 1}]
+    assert vim.vars.get('python') == [1, 2, {'3': 1}]
+
+    del vim.vars['python']
+    with pytest.raises(KeyError):
+        vim.vars['python']
+    assert vim.eval('exists("g:python")') == 0
+
+    with pytest.raises(KeyError):
+        del vim.vars['python']
+
+    assert vim.vars.get('python', 'default') == 'default'
 
 
 def test_options(vim):
-    assert vim.options['listchars'] == 'tab:> ,trail:-,nbsp:+'
-    vim.options['listchars'] = 'tab:xy'
-    assert vim.options['listchars'] == 'tab:xy'
+    assert vim.options['background'] == 'dark'
+    vim.options['background'] = 'light'
+    assert vim.options['background'] == 'light'
+
+
+def test_local_options(vim):
+    assert vim.windows[0].options['foldmethod'] == 'manual'
+    vim.windows[0].options['foldmethod'] = 'syntax'
+    assert vim.windows[0].options['foldmethod'] == 'syntax'
 
 
 def test_buffers(vim):
@@ -178,6 +209,7 @@
     assert cwd_python == cwd_vim
     assert cwd_python != cwd_before
 
+
 lua_code = """
 local a = vim.api
 local y = ...
@@ -194,15 +226,16 @@
    return a.nvim_buf_line_count(buf)
 end
 
-pynvimtest = {setbuf=setbuf,getbuf=getbuf}
+pynvimtest = {setbuf=setbuf, getbuf=getbuf}
 
 return "eggspam"
 """
 
+
 def test_lua(vim):
-  assert vim.exec_lua(lua_code, 7) == "eggspam"
-  assert vim.lua.pynvimtest_func(3) == 10
-  testmod = vim.lua.pynvimtest
-  buf = vim.current.buffer
-  testmod.setbuf(buf, ["a", "b", "c", "d"], async_=True)
-  assert testmod.getbuf(buf) == 4
+    assert vim.exec_lua(lua_code, 7) == "eggspam"
+    assert vim.lua.pynvimtest_func(3) == 10
+    lua_module = vim.lua.pynvimtest
+    buf = vim.current.buffer
+    lua_module.setbuf(buf, ["a", "b", "c", "d"], async_=True)
+    assert lua_module.getbuf(buf) == 4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/test/test_window.py 
new/pynvim-0.4.1/test/test_window.py
--- old/pynvim-0.3.2/test/test_window.py        2019-01-20 16:22:43.000000000 
+0100
+++ new/pynvim-0.4.1/test/test_window.py        2020-01-25 10:16:18.000000000 
+0100
@@ -1,3 +1,6 @@
+import pytest
+
+
 def test_buffer(vim):
     assert vim.current.buffer == vim.windows[0].buffer
     vim.command('new')
@@ -40,6 +43,17 @@
     vim.current.window.vars['python'] = [1, 2, {'3': 1}]
     assert vim.current.window.vars['python'] == [1, 2, {'3': 1}]
     assert vim.eval('w:python') == [1, 2, {'3': 1}]
+    assert vim.current.window.vars.get('python') == [1, 2, {'3': 1}]
+
+    del vim.current.window.vars['python']
+    with pytest.raises(KeyError):
+        vim.current.window.vars['python']
+    assert vim.eval('exists("w:python")') == 0
+
+    with pytest.raises(KeyError):
+        del vim.current.window.vars['python']
+
+    assert vim.current.window.vars.get('python', 'default') == 'default'
 
 
 def test_options(vim):
@@ -50,6 +64,10 @@
     assert vim.current.window.options['statusline'] == 'window-status'
     assert vim.options['statusline'] == ''
 
+    with pytest.raises(KeyError) as excinfo:
+        vim.current.window.options['doesnotexist']
+    assert excinfo.value.args == ("Invalid option name: 'doesnotexist'",)
+
 
 def test_position(vim):
     height = vim.windows[0].height
@@ -103,4 +121,4 @@
 
 
 def test_repr(vim):
-    assert repr(vim.current.window) == "<Window(handle=1001)>"
+    assert repr(vim.current.window) == "<Window(handle=1000)>"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pynvim-0.3.2/tox.ini new/pynvim-0.4.1/tox.ini
--- old/pynvim-0.3.2/tox.ini    2019-01-20 16:22:43.000000000 +0100
+++ new/pynvim-0.4.1/tox.ini    2020-01-25 10:16:18.000000000 +0100
@@ -1,16 +1,19 @@
 [tox]
 envlist =
-  py{27,34,35,36}-{asyncio,pyuv},pypy
-  py36-docs
+  py{27,34,35,36,37,38}-{asyncio,pyuv}-cov,pypy-cov
+  checkqa
 
 [testenv]
+extras = test
 deps =
-  pytest
-  pytest-xdist
   pytest-timeout
+  cov: pytest-cov
   pyuv: pyuv
+setenv =
+  cov: PYTEST_ADDOPTS=--cov=. {env:PYTEST_ADDOPTS:}
 passenv = PYTEST_ADDOPTS
-commands = python -m pytest {posargs}
+commands =
+  python -m pytest {posargs}
 
 [testenv:checkqa]
 deps =
@@ -18,7 +21,7 @@
   flake8-import-order
   flake8-docstrings
   pep8-naming
-commands = flake8 {posargs:pynvim}
+commands = flake8 {posargs:pynvim test}
 
 [testenv:docs]
 deps =

++++++ setup_version.patch ++++++
--- a/setup.py
+++ b/setup.py
@@ -35,7 +35,7 @@ if platform.python_implementation() != '
     install_requires.append('greenlet')
 
 setup(name='pynvim',
-      version='0.4.0',
+      version='0.4.1',
       description='Python client to neovim',
       url='http://github.com/neovim/pynvim',
       download_url='https://github.com/neovim/pynvim/archive/0.4.1.tar.gz',

Reply via email to