Skip to content

Commit

Permalink
fix pydantic compatibility issues
Browse files Browse the repository at this point in the history
  • Loading branch information
imryche committed Jan 2, 2024
1 parent 1b223bc commit 0c43b68
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 95 deletions.
20 changes: 7 additions & 13 deletions blockkit/blocks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, List, Optional, Union
from typing import List, Optional, Union

from pydantic import Field, model_validator
from pydantic.networks import HttpUrl
Expand Down Expand Up @@ -216,16 +216,10 @@ def __init__(
)

_validate_text = validator("text", validate_text_length, max_length=3000)
_validate_fields = validator(
"fields_", validate_list_text_length, max_length=2000
)

@model_validator(mode="before")
def _validate_values(cls, values: Dict) -> Dict:
text = values.get("text")
fields = values.get("fields")

if text is None and fields is None:
raise ValueError("You must provide either text or fields")
_validate_fields = validator("fields_", validate_list_text_length, max_length=2000)

return values
@model_validator(mode="after")
def _validate_values(self) -> "Section":
if not self.text and not self.fields_:
raise ValueError("You must provide either text or fields.")
return self
113 changes: 58 additions & 55 deletions blockkit/elements.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import itertools
from datetime import date, datetime, time
from typing import Dict, List, Optional, Union
from typing import List, Optional, Union

from pydantic import AnyUrl, Field, model_validator
from pydantic.networks import HttpUrl
Expand Down Expand Up @@ -113,16 +113,15 @@ def __init__(
focus_on_load=focus_on_load,
)

@model_validator(mode="before")
def _validate_values(cls, values: Dict) -> Dict:
initial_options = values.get("initial_options")
options = values.get("options")
@model_validator(mode="after")
def _validate_values(self) -> "Checkboxes":
if not self.initial_options:
return self

if initial_options is not None:
for initial_option in initial_options:
if initial_option not in options:
raise ValueError(f"Option {initial_option} isn't within {options}")
return values
for initial_option in self.initial_options:
if initial_option not in self.options:
raise ValueError(f"Option {initial_option} isn't within {self.options}")
return self


class DatePicker(FocusableElement):
Expand Down Expand Up @@ -176,6 +175,7 @@ def __init__(
focus_on_load=focus_on_load,
)


class Image(Component):
type: str = "image"
image_url: HttpUrl
Expand All @@ -196,7 +196,9 @@ class Select(FocusableElement):

class StaticSelectBase(Select):
options: Optional[List[PlainOption]] = Field(None, min_length=1, max_length=100)
option_groups: Optional[List[OptionGroup]] = Field(None, min_length=1, max_length=100)
option_groups: Optional[List[OptionGroup]] = Field(
None, min_length=1, max_length=100
)


class StaticSelect(StaticSelectBase):
Expand Down Expand Up @@ -224,27 +226,28 @@ def __init__(
focus_on_load=focus_on_load,
)

@model_validator(mode="before")
def _validate_values(cls, values: Dict) -> Dict:
initial_option = values.get("initial_option")
options = values.get("options")
option_groups = values.get("option_groups")

if not any((options, option_groups)) or options and option_groups:
raise ValueError("You must provide either options or option_groups")

if None not in (initial_option, options) and initial_option not in options:
raise ValueError(f"Option {initial_option} isn't within {options}")

if None not in (initial_option, option_groups):
if initial_option not in itertools.chain(
*[og.options for og in option_groups]
@model_validator(mode="after")
def _validate_values(self) -> "StaticSelect":
if bool(self.options) == bool(self.option_groups):
raise ValueError("You must provide either options or option_groups.")

if (
self.initial_option
and self.options
and self.initial_option not in self.options
):
raise ValueError(
f"Option {self.initial_option} isn't within {self.options}"
)

if self.initial_option and self.option_groups:
if self.initial_option not in itertools.chain(
*[og.options for og in self.option_groups]
):
raise ValueError(
f"Option {initial_option} isn't within {option_groups}"
f"Option {self.initial_option} isn't within {self.option_groups}"
)

return values
return self


class MultiStaticSelect(StaticSelectBase):
Expand Down Expand Up @@ -275,29 +278,26 @@ def __init__(
focus_on_load=focus_on_load,
)

@model_validator(mode="before")
def _validate_values(cls, values: Dict) -> Dict:
initial_options = values.get("initial_options")
options = values.get("options")
option_groups = values.get("option_groups")
@model_validator(mode="after")
def _validate_values(self) -> "MultiStaticSelect":
if bool(self.options) == bool(self.option_groups):
raise ValueError("You must provide either options or option_groups.")

if not any((options, option_groups)) or options and option_groups:
raise ValueError("You must provide either options or option_groups")

if None not in (initial_options, options):
for initial_option in initial_options:
if initial_option not in options:
raise ValueError(f"Option {initial_option} isn't within {options}")
if self.initial_options and self.options:
for initial_option in self.initial_options:
if initial_option not in self.options:
raise ValueError(
f"Option {initial_option} isn't within {self.options}"
)

if None not in (initial_options, option_groups):
groups_options = itertools.chain(*[og.options for og in option_groups])
for initial_option in initial_options:
if self.initial_options and self.option_groups:
groups_options = itertools.chain(*[og.options for og in self.option_groups])
for initial_option in self.initial_options:
if initial_option not in groups_options:
raise ValueError(
f"Option {initial_option} isn't within {option_groups}"
f"Option {initial_option} isn't within {self.option_groups}"
)

return values
return self


class ExternalSelectBase(Select):
Expand Down Expand Up @@ -591,14 +591,17 @@ def __init__(
focus_on_load=focus_on_load,
)

@model_validator(mode="before")
def _validate_values(cls, values):
initial_option = values.get("initial_option")
options = values.get("options")

if initial_option is not None and initial_option not in options:
raise ValueError(f"Option {initial_option} isn't within {options}")
return values
@model_validator(mode="after")
def _validate_values(self) -> "RadioButtons":
if (
self.initial_option
and self.options
and self.initial_option not in self.options
):
raise ValueError(
f"Option {self.initial_option} isn't within {self.options}"
)
return self


class TimePicker(FocusableElement):
Expand Down
16 changes: 11 additions & 5 deletions blockkit/objects.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, List, Optional, Union
from typing import List, Optional, Union

from pydantic import AnyUrl, Field, model_validator

Expand Down Expand Up @@ -149,8 +149,14 @@ def __init__(
exclude_bot_users=exclude_bot_users,
)

@model_validator(mode="before")
def _validate_values(cls, values: Dict) -> Dict:
if not any(values.values()):
@model_validator(mode="after")
def _validate_values(self) -> "Filter":
if not any(
[
self.include,
self.exclude_external_shared_channels,
self.exclude_bot_users,
]
):
raise ValueError("You should provide at least one argument.")
return values
return self
25 changes: 9 additions & 16 deletions blockkit/surfaces.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, List, Optional, Union
from typing import List, Optional, Union

from pydantic import Field, model_validator

Expand Down Expand Up @@ -87,14 +87,11 @@ def __init__(
_validate_close = validator("close", validate_text_length, max_length=24)
_validate_submit = validator("submit", validate_text_length, max_length=24)

@model_validator(mode="before")
def _validate_values(cls, values: Dict) -> Dict:
blocks = values.get("blocks")
submit = values.get("submit")

if blocks and not submit and Input in (type(b) for b in values["blocks"]):
@model_validator(mode="after")
def _validate_values(self) -> "Modal":
if self.blocks and not self.submit and Input in (type(b) for b in self.blocks):
raise ValueError("submit is required when an Input is within blocks")
return values
return self


class WorkflowStep(View):
Expand Down Expand Up @@ -126,12 +123,8 @@ def __init__(
):
super().__init__(text=text, blocks=blocks)

@model_validator(mode="before")
def _validate_values(cls, values: Dict) -> Dict:
text = values.get("text")
fields = values.get("blocks")

if text is None and fields is None:
@model_validator(mode="after")
def _validate_values(self) -> "Message":
if not self.text and not self.blocks:
raise ValueError("You must provide either text or blocks")

return values
return self
8 changes: 4 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pytest==6.2.5
pydantic==2.3.0
black==21.11b1
python-dateutil==2.8.2
pytest>=6.0,<7.0
pydantic>=2,<3
black>=20.0,<22.0
python-dateutil>=2.8,<3.0
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
REQUIRES_PYTHON = ">=3.7.0"
VERSION = "1.6.1"

REQUIRED = ["pydantic>=2"]
REQUIRED = ["pydantic>=2,<3"]
EXTRAS = {"gen": ["black"]}

here = os.path.abspath(os.path.dirname(__file__))
Expand Down
7 changes: 6 additions & 1 deletion tests/test_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,12 @@ def test_builds_datetimepicker():
assert DatetimePicker(
action_id="action_id",
initial_date_time=datetime(
year=2023, month=1, day=2, hour=3, minute=4, tzinfo=gettz("America/New_York"),
year=2023,
month=1,
day=2,
hour=3,
minute=4,
tzinfo=gettz("America/New_York"),
),
confirm=Confirm(
title=PlainText(text="title"),
Expand Down

0 comments on commit 0c43b68

Please sign in to comment.