Skip to content

Commit a06146f

Browse files
authored
fix: vim.eval should treat Vim boolean as Python bool #603
Problem: - In Nvim, Vim bool is evaled to string, but in Vim 8+, it is evaled to Python bool. So pynvim's legacy vim.eval is not compatible with Vim 8+. - Previously, the argument against compatibility with Vim 8+ in this aspect is that adding checking for Python bool would be slower. But if we use `type(obj)` with operator `is`, it would be 3% ~ 80% faster than the current num_to_str implementation (more complex obj seems to have more performance gain). This is the script I use to benchmark ```python import timeit num_types = (int, float) def num_to_str(obj): if isinstance(obj, num_types): return str(obj) else: return obj def num_to_str2(obj): obj_type = type(obj) if obj_type is int or obj_type is float: return str(obj) else: return obj def num_to_str3(obj): obj_type = type(obj) if obj_type == int or obj_type == float: return str(obj) else: return test_cases = [ (100, "Integer"), (10.5, "Float"), (True, "Boolean"), ("hello", "String"), ([1, 2], "List"), ({'a': 1, 'b': 2}, "Dict"), # Restored! ] def run_benchmark(): # Using a fixed format string for perfect alignment fmt = "{:<15} | {:<10} | {:<18} | {:<18} | {:>9}" header = fmt.format("Input", "Type", "isinstance (s)", "type is (s)", "type == (s)") print("\n" + header) print("-" * len(header)) for val, label in test_cases: setup = "from __main__ import num_to_str, num_to_str2, num_types" # Number of loops: 1,000,000 t1 = timeit.timeit("num_to_str(obj)", setup=setup, number=1_000_000, globals={'obj': val, **globals()}) t2 = timeit.timeit("num_to_str2(obj)", setup=setup, number=1_000_000, globals={'obj': val, **globals()}) t3 = timeit.timeit("num_to_str3(obj)", setup=setup, number=1_000_000, globals={'obj': val, **globals()}) display_val = str(val) if len(display_val) > 14: display_val = display_val[:11] + "..." print(fmt.format(display_val, label, f"{t1:.4f}", f"{t2:.4f}", f"{t3:.4f}")) if __name__ == "__main__": run_benchmark() ``` And the benchmark result: ``` Input | Type | isinstance (s) | type is (s) | type == (s) ------------------------------------------------------------------------------------ 100 | Integer | 0.1125 | 0.1095 | 0.1118 10.5 | Float | 0.3256 | 0.2512 | 0.2791 True | Boolean | 0.0731 | 0.0604 | 0.0788 hello | String | 0.1005 | 0.0600 | 0.0793 [1, 2] | List | 0.0940 | 0.0602 | 0.0755 {'a': 1, 'b... | Dict | 0.1080 | 0.0599 | 0.0759 ``` So clearly using `type(obj)` with `is` is the fastest way. Solution: - Use type(obj) with is operator is to check for exact number
1 parent fdaae82 commit a06146f

2 files changed

Lines changed: 2 additions & 4 deletions

File tree

README.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,6 @@ Pynvim defines some extensions over the vim python API:
6363

6464
See the [Python Plugin API](http://pynvim.readthedocs.io/en/latest/usage/python-plugin-api.html) documentation for usage of this new functionality.
6565

66-
### Known Issues
67-
- Vim evaluates `'v:<bool>'` to `<class 'bool'>`, whereas neovim evaluates to `<class 'str'>`. This is expected behaviour due to the way booleans are implemented in python as explained [here](https://github.com/neovim/pynvim/issues/523#issuecomment-1495502011).
68-
6966
Development
7067
-----------
7168

pynvim/plugin/script_host.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ def writelines(self, seq):
195195

196196

197197
def num_to_str(obj):
198-
if isinstance(obj, num_types):
198+
obj_type = type(obj)
199+
if obj_type is int or obj_type is float:
199200
return str(obj)
200201
else:
201202
return obj

0 commit comments

Comments
 (0)