|
13 | 13 | import re
|
14 | 14 | import traitlets
|
15 | 15 | import collections.abc
|
| 16 | +from pydantic import BaseModel |
16 | 17 |
|
17 | 18 | # We are providing some compatibility for ipywidgets v7 and v8
|
18 | 19 | IS_VERSION_8 = version.parse(ipywidgets.__version__).major == 8
|
@@ -48,6 +49,15 @@ def as_tuple(obj):
|
48 | 49 | return (obj,)
|
49 | 50 |
|
50 | 51 |
|
| 52 | +def convert_pydantic_to_schema(model) -> dict: |
| 53 | + """ |
| 54 | + Converts a Pydantic model class to its corresponding JSON schema. |
| 55 | + """ |
| 56 | + if isinstance(model, type) and issubclass(model, BaseModel): |
| 57 | + return model.model_json_schema() |
| 58 | + return model |
| 59 | + |
| 60 | + |
51 | 61 | def minmax_schema_rule(widget, schema):
|
52 | 62 | """This implements the minimum/maximum rules
|
53 | 63 |
|
@@ -122,6 +132,7 @@ def __init__(
|
122 | 132 | A function that is used to sort the keys in a dictionary. Defaults to
|
123 | 133 | the built-in sorted, but is no-op if sorted raises a TypeError.
|
124 | 134 | """
|
| 135 | + schema = convert_pydantic_to_schema(schema) |
125 | 136 | # Make sure that the given schema is valid
|
126 | 137 | Draft7Validator.check_schema(schema)
|
127 | 138 |
|
@@ -449,6 +460,25 @@ def pattern_checker(val):
|
449 | 460 | def _register_observer(h, n, t):
|
450 | 461 | widget.observe(h, names=n, type=t)
|
451 | 462 |
|
| 463 | + warning_label = ipywidgets.Label( |
| 464 | + "", layout=ipywidgets.Layout(color="red", display="none") |
| 465 | + ) |
| 466 | + |
| 467 | + def _observer(change): |
| 468 | + if not pattern_checker(widget.value): |
| 469 | + pattern = schema.get("pattern", ".*") |
| 470 | + warning_label.value = ( |
| 471 | + f"Warning: Input does not match the specified pattern" |
| 472 | + ) |
| 473 | + warning_label.layout.display = "block" |
| 474 | + change.owner.layout.border = "2px solid red" |
| 475 | + else: |
| 476 | + change.owner.layout.border = "none" |
| 477 | + warning_label.value = "" |
| 478 | + warning_label.layout.display = "none" |
| 479 | + |
| 480 | + widget.observe(_observer, names="value", type="change") |
| 481 | + |
452 | 482 | def _setter(_d):
|
453 | 483 | if pattern_checker(_d):
|
454 | 484 | widget.value = _d
|
@@ -519,7 +549,7 @@ def _getter():
|
519 | 549 | getter=_getter,
|
520 | 550 | setter=_setter,
|
521 | 551 | resetter=_resetter,
|
522 |
| - widgets=[box], |
| 552 | + widgets=[box, warning_label], |
523 | 553 | register_observer=_register_observer,
|
524 | 554 | )
|
525 | 555 |
|
@@ -967,12 +997,18 @@ def _resetter():
|
967 | 997 | # Initially call the resetter
|
968 | 998 | _resetter()
|
969 | 999 |
|
| 1000 | + def _getter(): |
| 1001 | + result = [h.getter() for h in elements[:element_size]] |
| 1002 | + if schema.get("uniqueItems", False): |
| 1003 | + result = list(set(result)) |
| 1004 | + return result |
| 1005 | + |
970 | 1006 | wrapped_vbox[0] = self._wrap_description(
|
971 | 1007 | wrapped_vbox[0], schema.get("description", None)
|
972 | 1008 | )
|
973 | 1009 |
|
974 | 1010 | return self.construct_element(
|
975 |
| - getter=lambda: [h.getter() for h in elements[:element_size]], |
| 1011 | + getter=_getter, |
976 | 1012 | setter=_setter,
|
977 | 1013 | resetter=_resetter,
|
978 | 1014 | widgets=wrapped_vbox,
|
|
0 commit comments