jedi icon indicating copy to clipboard operation
jedi copied to clipboard

Inference of typing.Annotated

Open dsalisbury opened this issue 4 years ago • 3 comments

Hey!

I've got a project where I'm looking at using the Annotated type annotation which was new in Python 3.9 (and backported through typing_extensions).

typing.Annotated

A type, introduced in PEP 593 (Flexible function and variable annotations), to decorate existing types with context-specific metadata (possibly multiple pieces of it, as Annotated is variadic). Specifically, a type T can be annotated with metadata x via the typehint Annotated[T, x]. This metadata can be used for either static analysis or at runtime. If a library (or tool) encounters a typehint Annotated[T, x] and has no special logic for metadata x, it should ignore it and simply treat the type as T.

I'd expect Jedi to take the naive route and go with "simply treat the type as T". However it's not something which Jedi handles at present as I've confirmed with a fresh clone of the repo today.

How might I go about adding support for this in Jedi? I'm new to this codebase so some pointers would be appreciated.

Or is this even a Jedi thing in the first place? is something else missing a definition about Annotated?

I was looking at how to implement it and ended up starting with a basic test which fails as expected. Does this look right as a starting point?

from typing import Annotated

# This is just a dummy and very meaningless thing to use with to the Annotated
# type hint
class Foo:
    pass

class A:
    pass


def annotated_function_params(
    basic: Annotated[str, Foo()],
    obj: A,
    annotated_obj: Annotated[A, Foo()],
):
    #? str
    basic

    #? A()
    obj

    #? A()
    annotated_obj

The first and third tests there fail as expected with this sort of message:

E       AssertionError:
E         Test <IntegrationTestCase: /src/test/completion/pep0593_annotations.py:24 '    annotated_obj'> failed.
E         actual  =
E             set()
E         desired =
E             {'completion.pep0593_annotations.A()'}
E
E       assert set() == {'completion....otations.A()'}
E         Extra items in the right set:
E         'completion.pep0593_annotations.A()'
E         Full diff:
E         - {'completion.pep0593_annotations.A()'}
E         + set()

dsalisbury avatar Feb 08 '21 06:02 dsalisbury

I'm pretty sure this is not handled yet. Want to try to fix it? I'm happy to assist.

The right place to work on this is definitely jedi/inference/gradual/typing.py. It's probably a really small fix (20 lines). The hard part is to understand what you need to do.

davidhalter avatar Feb 08 '21 20:02 davidhalter

@davidhalter Any hints on where to start in jedi/inference/gradual/typing.py?

I think the intended behavior would be to just return the type of the first parameter to the Annotated generic. Would this be treated the sameway as ClassVar?

https://github.com/davidhalter/jedi/blob/dcea842ac237b51263c29a252dba9b650fc799ff/jedi/inference/gradual/typing.py#L116-L118

asford avatar Apr 09 '21 18:04 asford

I think the intended behavior would be to just return the type of the first parameter to the Annotated generic. Would this be treated the sameway as ClassVar?

Oh yeah, good find! It's pretty much that I guess. Feel free to create a PR for it :)

davidhalter avatar Apr 09 '21 19:04 davidhalter