-
Notifications
You must be signed in to change notification settings - Fork 144
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
[Tidy] Bump to Pydantic V2 #917
base: main
Are you sure you want to change the base?
Conversation
…feature/pydantic_v2
…edCallable fields This for now involves using `from pydantic.json_schema import SkipJsonSchema` which seems like a recommended and stable solution. We can try a more advanced approach if needs be
For now this does not include any arguments, as in the documentation the parent model works on a string type description, and presumably infers the correct model by name. Will need to investigate this behaviour closer, but for now this commit allows unit tests to run!
…ern for Pydantic v2 compatibility
…y code and use Pydantic v2 imports This does not include models yet where unit test instantiation fails, mainly due to use of validator etc. It also does not include tests yet
…rts to Pydantic v2
…et checks This replaces the each_item validation of the old style validator. Tested manually if it generally works
… use field_validator for Pydantic v2
…foreValidator to avoid validator So far did not fully eliminate validator due to it being present still for Layout - it needs to be determined whether we want a model validator here
This is a larger change that refactors the previous methods of __get_valdators__ The crux of this change is that it was not anymore possible to pass the field info to the validator without having acces to the model class. See more here: pydantic/pydantic#10903
…datate on entire targets list Additionally change the dev apps a little
At this point, all running code should be free of V1 nad only draw upon V2. This does not means that all docs have been cleaned, or that there is not usage of deprecated V2 functionality The next big challenge will be to get the add_type correct
This mostly involves fixing changed validation messages. There was also the case that we do NOT coerce numbers into strings anymore (this is technically slightly breaking)
Similar as before, with the change to not coercing strings as the main change
for more information, see https://pre-commit.ci
View the example dashboards of the current commit live on PyCafe ☕ 🚀Updated on: 2024-12-03 15:53:17 UTC Link: vizro-core/examples/dev/ Link: vizro-core/examples/scratch_dev |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general it looks 💯 A few comments for now. I'll look at types.py and _base.py tomorrow morning (or whenever you think it's ready).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess these demo apps need manually checking because we don't have unit tests for them. It looks like they don't do anything complicated with pydantic though so hopefully they just work already.
Dashboard.update_forward_refs(Page=Page, Navigation=Navigation) | ||
NavBar.update_forward_refs(NavLink=NavLink) | ||
NavLink.update_forward_refs(Accordion=Accordion) | ||
Tabs.model_rebuild() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If these are removed entirely I presume things don't work?
def set_id(cls, id) -> str: | ||
return id or model_manager._generate_id() | ||
|
||
@_log_call | ||
def __init__(self, **data: Any): | ||
def __init__(self, **data: Any): # TODO: model_post_init |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yesss 👍 After we do this then hopefully we get much better tab completion of model fields in IDEs. It would be worth checking this and advertising somewhere in the docs (maybe you need to install some plugin).
components: list[_FormComponentType] | ||
layout: Layout = None # type: ignore[assignment] | ||
components: conlist( | ||
Annotated[_FormComponentType, BeforeValidator(check_captured_callable), Field(...)], min_length=1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the Field(...)
really needed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(same comment applies elsewhere)
components: conlist( | ||
Annotated[_FormComponentType, BeforeValidator(check_captured_callable), Field(...)], min_length=1 | ||
) # since no default, can skip validate_default | ||
layout: Optional[Layout] = None # type: ignore[assignment] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
layout: Optional[Layout] = None # type: ignore[assignment] | |
layout: Optional[Layout] = None |
hopefully?
@@ -31,15 +31,13 @@ class Container(VizroBaseModel): | |||
""" | |||
|
|||
type: Literal["container"] = "container" | |||
components: list[ComponentType] | |||
components: conlist( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this (and others) done as conlist
while others like RangeSlider.value
and Container.tabs
aren't?
@@ -30,45 +67,20 @@ class Parameter(VizroBaseModel): | |||
""" | |||
|
|||
type: Literal["parameter"] = "parameter" | |||
targets: list[str] = Field(..., description="Targets in the form of `<target_component>.<target_argument>`.") | |||
targets: Annotated[ # TODO: check if the double annotation is the best way to do this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks weird now because the each_item=True
is handled differently, right?
Ideally there would be a nicer way to do this that leaves the check_dot_notation
function etc. inside the Parameter
class.
@@ -24,7 +24,7 @@ def validate_min_length(cls, value): | |||
return value | |||
|
|||
|
|||
def check_captured_callable(cls, value): | |||
def check_captured_callable(value): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def check_captured_callable(value): | |
def check_captured_callable_mode(value): |
The name of this (which I originally chose) confused me - I think we should rename it.
@@ -26,9 +26,7 @@ class Tabs(VizroBaseModel): | |||
""" | |||
|
|||
type: Literal["tabs"] = "tabs" | |||
tabs: list[Container] | |||
|
|||
_validate_tabs = validator("tabs", allow_reuse=True, always=True)(validate_min_length) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remember to remove this validate_min_length
(and any other no longer used functions) from the source file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few small comments on CapturedCallable
but generally looks great 👍
# At this point captured_callable is CapturedCallable or invalid type. Check it is in fact CapturedCallable | ||
# and do final checks: | ||
yield cls._check_type | ||
def __get_pydantic_core_schema__(cls, source: Any, handler: Any) -> cs.core_schema.CoreSchema: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you remind me why we need this at all?
return cs.core_schema.no_info_plain_validator_function(cls.core_validation) | ||
|
||
@staticmethod | ||
def core_validation(value: Any): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see this is called in the above method but when does it actually get used?
if not isinstance(value, CapturedCallable): | ||
raise ValueError(f"Expected CustomType, got {type(value)}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if not isinstance(value, CapturedCallable): | |
raise ValueError(f"Expected CustomType, got {type(value)}") | |
if not isinstance(value, CapturedCallable): | |
raise ValueError(f"Expected CapturedCallable, got {type(value)}") |
def validate_captured_callable(cls, value, info: ValidationInfo): | ||
# TODO: We may want to double check on the mechanism of how field info is brought to | ||
field_info = cls.model_fields[info.field_name] | ||
return CapturedCallable._validate_captured_callable(value, field_info) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return CapturedCallable._validate_captured_callable(value, field_info) | |
return CapturedCallable._validate_captured_callable(value, field_info.json_schema_extra]) |
And rewrite signatures for the validators accordingly (we could even do a TypedDict
annotation to show the expected format of json_schema_extra
). Unless you think this is a bad idea for some reason. Generally speaking I would pass the minimum required information between these functions.
Description
In draft so far - I tried to keep as clear as possible commit messages - they should tell the whole story.
Screenshot
Notice
I acknowledge and agree that, by checking this box and clicking "Submit Pull Request":