Skip to content

Commit

Permalink
Fix type-based code completion for some LSPs
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ikalnytskyi committed Oct 5, 2023
1 parent a133f7d commit 1a98acc
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 8 deletions.
7 changes: 7 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
`````

Expand Down
11 changes: 3 additions & 8 deletions src/picobox/_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -113,7 +113,7 @@ def __repr__(self):
name = "0x%x" % id(self)
return "<Stack (%s)>" % 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
Expand Down Expand Up @@ -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")
Expand Down

0 comments on commit 1a98acc

Please sign in to comment.