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

✨ Add autocomplete support for VS Code, via dataclass_transform #2721

Merged
merged 13 commits into from Sep 6, 2021
Merged
1 change: 1 addition & 0 deletions changes/2721-tiangolo.md
@@ -0,0 +1 @@
Add support for autocomplete in VS Code via `__dataclass_transform__`
17 changes: 16 additions & 1 deletion pydantic/main.py
Expand Up @@ -28,7 +28,7 @@
from .class_validators import ValidatorGroup, extract_root_validators, extract_validators, inherit_validators
from .error_wrappers import ErrorWrapper, ValidationError
from .errors import ConfigError, DictError, ExtraError, MissingError
from .fields import MAPPING_LIKE_SHAPES, ModelField, ModelPrivateAttr, PrivateAttr, Undefined
from .fields import MAPPING_LIKE_SHAPES, Field, FieldInfo, ModelField, ModelPrivateAttr, PrivateAttr, Undefined
from .json import custom_pydantic_encoder, pydantic_encoder
from .parse import Protocol, load_file, load_str_bytes
from .schema import default_ref_template, model_schema
Expand Down Expand Up @@ -102,6 +102,20 @@ def __call__(self, schema: Dict[str, Any], model_class: Type['Model']) -> None:
except AttributeError:
compiled = False


_T = TypeVar('_T')


def __dataclass_transform__(
*,
eq_default: bool = True,
order_default: bool = False,
kw_only_default: bool = False,
samuelcolvin marked this conversation as resolved.
Show resolved Hide resolved
field_descriptors: Tuple[Union[type, Callable[..., Any]], ...] = (()),
) -> Callable[[_T], _T]:
return lambda a: a


__all__ = 'BaseConfig', 'BaseModel', 'Extra', 'compiled', 'create_model', 'validate_model'


Expand Down Expand Up @@ -223,6 +237,7 @@ def hash_function(self_: Any) -> int:
_is_base_model_class_defined = False


@__dataclass_transform__(kw_only_default=True, field_descriptors=(Field, FieldInfo))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any possibility to add the frozen argument which should equal config.frozen or not config.allow_mutation?

I can't see how this is possible but @erictraut suggested it should be.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can pass frozen=True when constructing a class that derives from ModelBase, like this:

class CustomerModel(ModelBase, frozen=True):

That was based on a suggestion you made in our email discussion — or at least how I interpreted your suggestion. Did I understand you correctly?

All classes are assumed to be non-frozen unless otherwise specified. There is a request from the attrs maintainers to provide a way to specify a frozen_default option so they can default to frozen=True if it is not specified. If that would also be useful for pydantic, it would bolster the case for adding it to the spec.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that's awesome. I've just tried it and it's already working!

This looks great then, the only problem I can see is the type strictness/laxness which is being discussed on microsoft/pyright#1782

Copy link
Member Author

@tiangolo tiangolo May 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EDIT: Dang it, I didn't see all your previous conversation above this message, I guess I had a stale window. Sorry.

EDIT 2: discard this message pretty much entirely, already covered above and below. 🤦 😂


Old message below:

I didn't see a way to do it from what I read in the spec.

If I understand correctly, it would be possible for Pyright (and the spec) to understand if the model was created like:

from pydantic import BaseModel

class Fish(BaseModel, frozen=True):
    title: str

...which is currently not supported by pydantic (although I think sounds interesting).

What is currently supported by pydantic (but I understand not by Pyright nor the spec) is:

from pydantic import BaseModel

class Fish(BaseModel):
    title: str
    class Config:
        frozen = True

I'll wait for @erictraut's input in case it's possible to support pydantic's internal class Config in a way I didn't realize.

Copy link
Member

@PrettyWood PrettyWood May 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class Fish(BaseModel, frozen=True):
    title: str

is actually supported in 1.8 @tiangolo 😉 Config arguments can now be passed as class kwargs thanks to @MrMrRobat's amazing work in #2356

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dang it again! 🤦 😂 pydantic has evolved so much and I haven't noticed everything! 😅

Thanks for the clarification @PrettyWood, that's awesome! 🎉

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this with v1.9 and Pyright does not seem to recognise frozen:

# pyright: strict

from typing import Hashable


from pydantic import BaseModel

class Foo(BaseModel, frozen=True):
    pass

foo: Hashable = Foo()  # Error: Expression of type "Foo" cannot be assigned to declared type "Hashable"
reveal_type(Foo.__hash__)  # Type of "Foo.__hash__" is "None"


from dataclasses import dataclass

@dataclass(frozen=True)
class Bar:
    pass

bar: Hashable = Bar()  # No error
reveal_type(Bar.__hash__)  # Type of "Bar.__hash__" is "(self: Bar) -> int"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, this probably needs a new issue.

I've no idea if this is something pydantic can help with, or an issue with pyright. @erictraut any idea?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class ModelMetaclass(ABCMeta):
@no_type_check # noqa C901
def __new__(mcs, name, bases, namespace, **kwargs): # noqa C901
Expand Down