Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancing editor completion for BaseModel.__init__ #3475

Closed
elda27 opened this issue Dec 2, 2021 Discussed in #3263 · 7 comments
Closed

Enhancing editor completion for BaseModel.__init__ #3475

elda27 opened this issue Dec 2, 2021 Discussed in #3263 · 7 comments

Comments

@elda27
Copy link

elda27 commented Dec 2, 2021

Discussed in #3263

Originally posted by elda27 September 26, 2021
Currently, BaseModel.__init__ takes **data as an argument.
I would like to set __annotations__ for editor complementions.
I seem that BaseModel.__init__.__annotations__ can be set in ModelMetaclass.

Is this change acceptable?
If it acceptable, I would like to contribute.

I posted three months ago.
Arguments of __init__ doesn't work code completion on the VSCode now.

Current behavior
type_hint-before

Expected proposing behavior
type_hint-after

I think __signature__ may work well since Python 3.9.
But practical annotation for __init__ should be __annotaions__ before Python 3.8.

I have already implemented this solution three months ago.
So I can contribute the solution.
Please consider my suggestion.

Following code is an example of changes at ModelMetaclass for code completion.

if not hasattr(cls, '__init__'): # __init__ is defined at subclass
    cls.__init__.__annotations__ = {
        k: field.type_
        for k, field in fields.items()
    }
    cls.__init__.__kwdefaults__ = {
        k: field.default for k, field in fields.items()
    }
    cls.__init__.__kwdefaults__.update({
        field.alias: field.default
        for field in fields.values()
        if field.has_alias
    })
# set __signature__ attr only for model class, but not for its instances
cls.__signature__ = ClassAttribute('__signature__', 
generate_model_signature(cls.__init__, fields, config))
@elda27 elda27 changed the title Enhancing editor completion for object construction Enhancing editor completion for BaseModel.__init__ Dec 3, 2021
@shughes-uk
Copy link

This would be wonderful, dataclasses do this and it's incredibly helpful

@Bobronium
Copy link
Contributor

Do you have a working PoC?

I don't think the approach you're going with will work. Correct me if I'm wrong, but IDE has no idea what fields in your code snippet are since it can only perform static analysis and won't run your code.

Also, this issue is addressed in #2698 and implemented in #2721, so I would propose closing #3263 and this issye and continue discussion in #2698 for better visibility.

@shughes-uk, dataclasses don't do any of this. IDEs just have a custom behaviour defined to support them since they're included the the standard library.

@shughes-uk
Copy link

shughes-uk commented Dec 4, 2021

Dangit I had hoped that wasn't the case but had my suspicions 😭 , glad to see that support will exist in the next release though

@elda27
Copy link
Author

elda27 commented Dec 6, 2021

@Bobronium
I understand the reference issues. Thanks!

By the way,
I assume this code appending ModelMetaclass#L288.
Also, it was not a PoC, but something I had already implemented in my environment, and it was working. (If you insert the code I wrote below the line, you can see the completion as shown in the image.)
However, this is not working as expected yesterday. (Actually, the arguments shown seem to be the ones that I inserted into the signature.)

In particular, I'm using pyright (backend of pylance), which may be not static analyser.
I'm no still reading the implementation of pyright, so I may be wrong, but I think pyright analyzes metaclasses and decorators.
For example, typing.TYPE_CHECKING described in PEP564 is a variable created to target static analysis tools.
I thought the same was true for meta classes.

But I think there may be something wrong in what I am saying.
And I need to go read other PEPs to get a better understanding.

@Bobronium
Copy link
Contributor

Bobronium commented Dec 6, 2021

Wow, if it does really work, authors of pyright have done a really great job. It's is a static file checker as well.

However, I still can't wrap my head around how it could be implemented. Are you sure it picks up __init__.__annotations__ and not class annotations that you define in body?

An easy check would be look like following (unfortunately don't use VSCode/pylance/pyright to test this):

from pydantic.main import BaseModel, ModelMetaclass

class AutocompleteMetaclass(ModelMetaclass):
    def __new__(*args, **kwargs):
        cls = super().__new__(*args, **kwargs)
        cls.__init__.__annotations__ = {'foo': int}
        return cls

class FancyAutocompleteModel(BaseModel, metaclass=AutocompleteMetaclass)
    ...

class Foo(FancyAutocompleteModel):
    bar: int

Foo()  # here you should have (bar: int) signature, if it really picks up annotations defined in metaclass

Also, keep in mind, that setting __annotations__ like this doesn't make sense, since you're always changing annotations of BaseModel.__init__. You have to create new __init__ for each model, which is not a trivial task. Here we struggled to do so and went with simpler solution of constructing and assigning a signature to the class: #1032 and #1034 (comment)

@miili
Copy link
Contributor

miili commented Jan 1, 2022

Any update here?

@samuelcolvin
Copy link
Member

#2721 is released with v1.9, I therefore think this is fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants