From 1a98acc2db1ae2e5bc4dd868ad96cf592c8c525a Mon Sep 17 00:00:00 2001 From: Ihor Kalnytskyi Date: Thu, 5 Oct 2023 01:21:43 +0300 Subject: [PATCH] Fix type-based code completion for some LSPs It turns out that `picobox.Stack()` is missing few key type annotations that breaks autocompletion for some LSP servers. For instance, in the following code example: @dataclasses.dataclass class Struct: value: int @picobox.pass_("value") def pack(value: int) -> Struct: return Struct(value=value) with picobox.push(picobox.Box()) as box: box.put("value", 42) value = pack(42).value there are two broken autocompletion points: * `.put()` is not completed for the `box` variable * `.value` is not completed for the struct type returned by `pack()` This patch fixes these scenarios by (1) annotating that `picobox.push()` returns a context manager, and (2) avoiding using too-much-dynamic expressions in `picobox.pass_()` which return type cannot be easily inferred. --- docs/index.rst | 7 +++++++ src/picobox/_stack.py | 11 +++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index aa5ee4b..34808a7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -359,6 +359,13 @@ Release Notes * Drop ``Python 3.7`` support. It reached its end-of-life recently. +* Fix ``@picobox.pass_()`` decorator issue when it was shadowing a return type + of the wrapped function breaking code completion in some LSP servers. + +* Fix ``picobox.push()`` context manager issue when it wasn't announcing + properly its return type breaking code completion in some LSP servers for the + returned object. + 3.0.0 ````` diff --git a/src/picobox/_stack.py b/src/picobox/_stack.py index 2afe1a8..93c8f95 100644 --- a/src/picobox/_stack.py +++ b/src/picobox/_stack.py @@ -93,7 +93,7 @@ def do(magic): .. versionadded:: 2.2 """ - def __init__(self, name: t.Text = None): + def __init__(self, name: t.Optional[t.Text] = None): self._name = name self._stack = [] self._lock = threading.Lock() @@ -113,7 +113,7 @@ def __repr__(self): name = "0x%x" % id(self) return "" % name - def push(self, box: Box, *, chain: bool = False): + def push(self, box: Box, *, chain: bool = False) -> t.ContextManager[Box]: """Push a :class:`Box` instance to the top of the stack. Returns a context manager, that will automatically pop the box from the @@ -172,12 +172,7 @@ def get(self, *args, **kwargs): @_copy_signature(Box.pass_) def pass_(self, *args, **kwargs): """The same as :meth:`Box.pass_` but for a box at the top.""" - # Box.pass_(topbox, *args, **kwargs) does not work in Python 2 because - # Box.pass_ is an unbound method, and unbound methods require a class - # instance as its first argument. Therefore, we need a workaround to - # extract a function without "method" wrapping, so we can pass anything - # as the first argument. - return vars(Box)["pass_"](self._topbox, *args, **kwargs) + return Box.pass_(self._topbox, *args, **kwargs) _instance = Stack("shared")