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

[Tidy] Bump to Pydantic V2 #917

Draft
wants to merge 33 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6f11390
Run bump-pydantic
maxschulz-COL Nov 12, 2024
2cdb045
Un-v1 actions.py
maxschulz-COL Nov 12, 2024
bbaeae5
Do not fail on warnings
maxschulz-COL Nov 12, 2024
b90f165
Run bump-pydantic
maxschulz-COL Nov 12, 2024
7ee047c
Un-v1 actions.py
maxschulz-COL Nov 12, 2024
b47fd4b
Do not fail on warnings
maxschulz-COL Nov 12, 2024
48957af
Merge branch 'feature/pydantic_v2' of github.com:mckinsey/vizro into …
maxschulz-COL Nov 13, 2024
28b3ffe
Refactor action and component models to use SkipJsonSchema for Captur…
maxschulz-COL Nov 13, 2024
15aba94
Refactor model forward references to use model_rebuild
maxschulz-COL Nov 13, 2024
c77df9b
Refactor id validator to field_validator
maxschulz-COL Nov 13, 2024
554afd3
Refactor validator usage in Dashboard model for Pydantic v2 compatibi…
maxschulz-COL Nov 13, 2024
ffef6dc
Refactor layout model to use field_validator and update regex to patt…
maxschulz-COL Nov 13, 2024
03f9b09
Refactor imports in various models to remove Pydantic v1 compatibilit…
maxschulz-COL Nov 13, 2024
4094e32
Remove Pydantic v1 compatibility code from test files and update impo…
maxschulz-COL Nov 13, 2024
b0f79f7
Refactor Parameter model to use AfterValidator via Annotated for targ…
maxschulz-COL Nov 14, 2024
a15e325
Refactor accordion model to remove Pydantic v1 compatibility code and…
maxschulz-COL Nov 15, 2024
b7dcd67
Refactor Dashboard and Navigation models to use Optional for nullable…
maxschulz-COL Nov 15, 2024
3663293
Refactor Page model to use Optional for layout field and update Pydan…
maxschulz-COL Nov 15, 2024
5052693
Refactor form, container, tabs, and page models to use conlist and Be…
maxschulz-COL Nov 15, 2024
34651ae
Enable CapturedCallable as type
maxschulz-COL Nov 22, 2024
e93d6da
Fix Parameter model twith nested Annotated and AfterValidator to vali…
maxschulz-COL Nov 22, 2024
180e75e
Merge branch 'main' into feature/pydantic_v2
maxschulz-COL Nov 22, 2024
7158c01
Refactor remaining v1 out of code base
maxschulz-COL Nov 22, 2024
286b039
Fix unit tests for Action and Checklist
maxschulz-COL Dec 2, 2024
335c0b8
Fix form component unit tests
maxschulz-COL Dec 3, 2024
4af444c
Merge branch 'main' into feature/pydantic_v2
maxschulz-COL Dec 3, 2024
6b77fbf
Fix more form component unit tests
maxschulz-COL Dec 3, 2024
8bff295
Fix remaining unit tests plus the Filter optional argument
maxschulz-COL Dec 3, 2024
f05c474
Fix remaining unit tests
maxschulz-COL Dec 3, 2024
906a448
Align unit tests for CapturedCallable with new way of validating it
maxschulz-COL Dec 3, 2024
ee6722e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 3, 2024
46035b3
Refactor BaseModel.add_type
maxschulz-COL Dec 6, 2024
d6b11d9
Fix unit tests for types
maxschulz-COL Dec 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 56 additions & 56 deletions vizro-core/examples/dev/app.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Example app to show all features of Vizro."""

from time import sleep
from typing import Literal, Optional
from typing import Optional

import dash_bootstrap_components as dbc
import pandas as pd
Expand Down Expand Up @@ -492,7 +492,7 @@
column="size",
selector=vm.Slider(title="Slider (Tips - size)", step=1, value=2),
),
vm.Filter(targets=["graph-stocks"], column="date", selector=vm.DatePicker(title="Date Picker (Stocks - date)")),
# vm.Filter(targets=["graph-stocks"], column="date", selector=vm.DatePicker(title="Date Picker (Stocks - date)")),
],
)

Expand Down Expand Up @@ -643,65 +643,65 @@ def my_custom_table(data_frame=None, chosen_columns: Optional[list[str]] = None)

# CUSTOM COMPONENTS -------------------------------------------------------------
# 1. Extend existing components
class TooltipNonCrossRangeSlider(vm.RangeSlider):
"""Custom numeric multi-selector `TooltipNonCrossRangeSlider`."""
# class TooltipNonCrossRangeSlider(vm.RangeSlider):
# """Custom numeric multi-selector `TooltipNonCrossRangeSlider`."""

type: Literal["other_range_slider"] = "other_range_slider"
# type: Literal["other_range_slider"] = "other_range_slider"

def build(self):
"""Extend existing component by calling the super build and update properties."""
range_slider_build_obj = super().build()
range_slider_build_obj[self.id].allowCross = False
range_slider_build_obj[self.id].tooltip = {"always_visible": True, "placement": "bottom"}
return range_slider_build_obj
# def build(self):
# """Extend existing component by calling the super build and update properties."""
# range_slider_build_obj = super().build()
# range_slider_build_obj[self.id].allowCross = False
# range_slider_build_obj[self.id].tooltip = {"always_visible": True, "placement": "bottom"}
# return range_slider_build_obj


vm.Filter.add_type("selector", TooltipNonCrossRangeSlider)
# vm.Filter.add_type("selector", TooltipNonCrossRangeSlider)


# 2. Create new custom component
class Jumbotron(vm.VizroBaseModel):
"""New custom component `Jumbotron`."""

type: Literal["jumbotron"] = "jumbotron"
title: str
subtitle: str
text: str

def build(self):
"""Build the new component based on Dash components."""
return html.Div([html.H2(self.title), html.H3(self.subtitle), html.P(self.text)])


vm.Page.add_type("components", Jumbotron)

custom_components = vm.Page(
title="Custom Components",
components=[
Jumbotron(
title="Custom component based on new creation",
subtitle="This is a subtitle to summarize some content.",
text="This is the main body of text of the Jumbotron.",
),
vm.Graph(
id="for_custom_chart",
figure=px.scatter(
iris,
title="Iris Dataset",
x="sepal_length",
y="petal_width",
color="sepal_width",
),
),
],
controls=[
vm.Filter(
column="sepal_length",
targets=["for_custom_chart"],
selector=TooltipNonCrossRangeSlider(title="Custom component based on extension"),
)
],
)
# class Jumbotron(vm.VizroBaseModel):
# """New custom component `Jumbotron`."""

# type: Literal["jumbotron"] = "jumbotron"
# title: str
# subtitle: str
# text: str

# def build(self):
# """Build the new component based on Dash components."""
# return html.Div([html.H2(self.title), html.H3(self.subtitle), html.P(self.text)])


# vm.Page.add_type("components", Jumbotron)

# custom_components = vm.Page(
# title="Custom Components",
# components=[
# Jumbotron(
# title="Custom component based on new creation",
# subtitle="This is a subtitle to summarize some content.",
# text="This is the main body of text of the Jumbotron.",
# ),
# vm.Graph(
# id="for_custom_chart",
# figure=px.scatter(
# iris,
# title="Iris Dataset",
# x="sepal_length",
# y="petal_width",
# color="sepal_width",
# ),
# ),
# ],
# controls=[
# vm.Filter(
# column="sepal_length",
# targets=["for_custom_chart"],
# selector=TooltipNonCrossRangeSlider(title="Custom component based on extension"),
# )
# ],
# )


# CUSTOM ACTIONS ---------------------------------------------------------------
Expand Down Expand Up @@ -778,7 +778,7 @@ def multiple_cards(data_frame: pd.DataFrame, n_rows: Optional[int] = 1) -> html.
components = [graphs, ag_grid, table, cards, figure, button, containers, tabs]
controls = [filters, parameters, selectors]
actions = [export_data_action, chart_interaction]
extensions = [custom_charts, custom_tables, custom_components, custom_actions, custom_figures]
extensions = [custom_charts, custom_tables, custom_actions, custom_figures] # custom_components

dashboard = vm.Dashboard(
title="Vizro Features",
Expand All @@ -805,7 +805,7 @@ def multiple_cards(data_frame: pd.DataFrame, n_rows: Optional[int] = 1) -> html.
"Extensions": [
"Custom Charts",
"Custom Tables",
"Custom Components",
# "Custom Components",
"Custom Actions",
"Custom Figures",
],
Expand Down
69 changes: 18 additions & 51 deletions vizro-core/examples/scratch_dev/app.py
Original file line number Diff line number Diff line change
@@ -1,69 +1,36 @@
from typing import List, Literal

from dash import html

import vizro.models as vm
import vizro.plotly.express as px
from dash import html
from vizro import Vizro
from vizro.models.types import ControlType

df_gapminder = px.data.gapminder()

from vizro._themes._color_values import COLORS

class ControlGroup(vm.VizroBaseModel):
"""Container to group controls."""
df = px.data.gapminder()

type: Literal["control_group"] = "control_group"
title: str
controls: List[ControlType] = []

def build(self):
return html.Div(
[html.H4(self.title), html.Hr()] + [control.build() for control in self.controls],
)


vm.Page.add_type("controls", ControlGroup)

page1 = vm.Page(
title="Relationship Analysis",
page = vm.Page(
title="Charts UI",
components=[
vm.Graph(id="scatter", figure=px.scatter(df_gapminder, x="gdpPercap", y="lifeExp", size="pop")),
vm.Card(text="Foo"),
vm.Graph(
id="gapminder",
figure=px.bar(
df,
x="continent",
y="gdpPercap",
),
),
],
controls=[
ControlGroup(
title="Group A",
controls=[
vm.Parameter(
id="this",
targets=["scatter.x"],
selector=vm.Dropdown(
options=["lifeExp", "gdpPercap", "pop"], multi=False, value="gdpPercap", title="Choose x-axis"
),
),
vm.Parameter(
targets=["scatter.y"],
selector=vm.Dropdown(
options=["lifeExp", "gdpPercap", "pop"], multi=False, value="lifeExp", title="Choose y-axis"
),
),
],
),
ControlGroup(
title="Group B",
controls=[
vm.Parameter(
targets=["scatter.size"],
selector=vm.Dropdown(
options=["lifeExp", "gdpPercap", "pop"], multi=False, value="pop", title="Choose bubble size"
),
)
],
vm.Filter(
targets=["gapminder"],
column="continent",
),
],
)

dashboard = vm.Dashboard(pages=[page1])
dashboard = vm.Dashboard(pages=[page])

if __name__ == "__main__":
Vizro().build(dashboard).run()
14 changes: 7 additions & 7 deletions vizro-core/examples/visual-vocabulary/custom_components.py
Copy link
Contributor

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.

Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

from typing import Literal

# try:
# from pydantic.v1 import Field
# except ImportError: # pragma: no cov
# from pydantic import Field
from urllib.parse import quote

import dash_bootstrap_components as dbc
import vizro.models as vm
from dash import dcc, html

try:
from pydantic.v1 import Field
except ImportError: # pragma: no cov
from pydantic import Field

from urllib.parse import quote
from pydantic import Field


class CodeClipboard(vm.VizroBaseModel):
Expand Down
2 changes: 1 addition & 1 deletion vizro-core/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ addopts = [
"--import-mode=importlib"
]
filterwarnings = [
"error",
# "error",
# Ignore until pandas is made compatible with Python 3.12:
"ignore:.*utcfromtimestamp:DeprecationWarning",
# Ignore until pandas 3 is released:
Expand Down
28 changes: 7 additions & 21 deletions vizro-core/src/vizro/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,13 @@
from ._layout import Layout
from ._page import Page

Tabs.update_forward_refs(Container=Container)
Container.update_forward_refs(
AgGrid=AgGrid, Button=Button, Card=Card, Figure=Figure, Graph=Graph, Layout=Layout, Table=Table, Tabs=Tabs
)
Page.update_forward_refs(
Accordion=Accordion,
AgGrid=AgGrid,
Button=Button,
Card=Card,
Container=Container,
Figure=Figure,
Filter=Filter,
Graph=Graph,
Parameter=Parameter,
Table=Table,
Tabs=Tabs,
)
Navigation.update_forward_refs(Accordion=Accordion, NavBar=NavBar, NavLink=NavLink)
Dashboard.update_forward_refs(Page=Page, Navigation=Navigation)
NavBar.update_forward_refs(NavLink=NavLink)
NavLink.update_forward_refs(Accordion=Accordion)
Tabs.model_rebuild()
Copy link
Contributor

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?

Container.model_rebuild()
Page.model_rebuild()
Navigation.model_rebuild()
Dashboard.model_rebuild()
NavBar.model_rebuild()
NavLink.model_rebuild()

__all__ = [
"Accordion",
Expand Down
31 changes: 18 additions & 13 deletions vizro-core/src/vizro/models/_action/_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@
import logging
from collections.abc import Collection, Mapping
from pprint import pformat
from typing import Any, Union
from typing import Annotated, Any, Union

# try:
# from pydantic.v1 import Field
# except ImportError: # pragma: no cov
# from pydantic import Field
from dash import Input, Output, State, callback, html

try:
from pydantic.v1 import Field, validator
except ImportError: # pragma: no cov
from pydantic import Field, validator
from pydantic import Field, StringConstraints, field_validator
from pydantic.json_schema import SkipJsonSchema

from vizro.models import VizroBaseModel
from vizro.models._models_utils import _log_call
from vizro.models.types import CapturedCallable
from vizro.models.types import CapturedCallable, validate_captured_callable

logger = logging.getLogger(__name__)

Expand All @@ -30,24 +31,28 @@ class Action(VizroBaseModel):

"""

function: CapturedCallable = Field(..., import_path="vizro.actions", mode="action", description="Action function.")
inputs: list[str] = Field(
function: SkipJsonSchema[CapturedCallable] = Field(
..., json_schema_extra={"mode": "action", "import_path": "vizro.actions"}, description="Action function."
)
inputs: list[Annotated[str, StringConstraints(pattern="^[^.]+[.][^.]+$")]] = Field(
[],
description="Inputs in the form `<component_id>.<property>` passed to the action function.",
regex="^[^.]+[.][^.]+$",
)
outputs: list[str] = Field(
outputs: list[Annotated[str, StringConstraints(pattern="^[^.]+[.][^.]+$")]] = Field(
[],
description="Outputs in the form `<component_id>.<property>` changed by the action function.",
regex="^[^.]+[.][^.]+$",
)

# Validators
_validate_function = field_validator("function", mode="before")(validate_captured_callable)

# TODO: Problem: generic Action model shouldn't depend on details of particular actions like export_data.
# Possible solutions: make a generic mapping of action functions to validation functions or the imports they
# require, and make the code here look up the appropriate validation using the function as key
# This could then also involve other validations currently only carried out at run-time in pre-defined actions, such
# as e.g. checking if the correct arguments have been provided to the file_format in export_data.
@validator("function")
@field_validator("function")
@classmethod
def validate_predefined_actions(cls, function):
if function._function.__name__ == "export_data":
file_format = function._arguments.get("file_format")
Expand Down
9 changes: 5 additions & 4 deletions vizro-core/src/vizro/models/_action/_actions_chain.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from functools import partial
from typing import Any, NamedTuple

try:
from pydantic.v1 import validator
except ImportError: # pragma: no cov
from pydantic import validator
from pydantic import validator

# try:
# from pydantic.v1 import validator
# except ImportError: # pragma: no cov
# from pydantic import validator
from vizro.models import Action, VizroBaseModel


Expand Down
Loading
Loading