Skip to content

Commit

Permalink
Introduce pg.typing.ValueSpec.transform and deprecate `pg.typing.Va…
Browse files Browse the repository at this point in the history
…lueSpec.user_validator`.

This CL supports field-level input value conversion, so users could employ custom input validation/conversion without overriding the `pg.Object.__init__` method. On top of existing `user_validator`'s functionality, the new `transform` field allows users to inject conversion logics during field declaration.

PiperOrigin-RevId: 561796317
  • Loading branch information
daiyip authored and pyglove authors committed Aug 31, 2023
1 parent a560ff7 commit a83c2ee
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 118 deletions.
2 changes: 2 additions & 0 deletions pyglove/core/symbolic/ref.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ def __new__(cls, value: Any, **kwargs):
@object_utils.explicit_method_override
def __init__(self, value: Any, **kwargs) -> None:
super().__init__(**kwargs)
if isinstance(value, Ref):
value = value.value
self._value = value

def _on_parent_change(
Expand Down
1 change: 1 addition & 0 deletions pyglove/core/symbolic/ref_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def test_new(self):
r = ref.Ref(A(1))
self.assertIsInstance(r, ref.Ref)
self.assertIsInstance(r.infer(), A)
self.assertEqual(ref.Ref(r), r)

def test_type_check(self):

Expand Down
26 changes: 16 additions & 10 deletions pyglove/core/typing/class_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,7 @@ class ValueSpec(object_utils.Formattable, object_utils.JSONConvertible):
| validation and | |
| transformation | |
+-----------------------+-------------------------------------------------+
| User customized value | :attr:`.user_validator` |
| validation | |
| User transform | :attr:`.transform` |
+-----------------------+-------------------------------------------------+
| Default value lookup | :attr:`.default` |
+-----------------------+-------------------------------------------------+
Expand Down Expand Up @@ -249,7 +248,7 @@ class ValueSpec(object_utils.Formattable, object_utils.JSONConvertible):
pg.typing.<ValueSpecClass>(
[validation-rules],
[default=<default>],
[user_validator=<user_validator>])
[transform=<transform>])
After creation, a ``ValueSpec`` object can be modified with chaining.
The code below creates an int specification with default value 1 and can
Expand Down Expand Up @@ -419,9 +418,8 @@ def annotation(self) -> Any:

@property
@abc.abstractmethod
def user_validator(
self) -> Optional[Callable[[Any], None]]:
"""Returns a user validator which is used for custom validation logic."""
def transform(self) -> Optional[Callable[[Any], Any]]:
"""Returns a transform that will be applied on the input before apply."""

@abc.abstractmethod
def is_compatible(self, other: 'ValueSpec') -> bool:
Expand Down Expand Up @@ -456,7 +454,8 @@ def apply(
allow_partial: bool = False,
child_transform: Optional[Callable[
[object_utils.KeyPath, 'Field', Any], Any]] = None,
root_path: Optional[object_utils.KeyPath] = None) -> Any:
root_path: Optional[object_utils.KeyPath] = None,
) -> Any:
"""Validates, completes and transforms the input value.
Here is the procedure of ``apply``::
Expand Down Expand Up @@ -694,7 +693,12 @@ def apply(
ValueError: if value is not acceptable, or value is MISSING_VALUE while
allow_partial is set to False.
"""
value = self._value.apply(value, allow_partial, transform_fn, root_path)
value = self._value.apply(
value,
allow_partial=allow_partial,
child_transform=transform_fn,
root_path=root_path)

if transform_fn:
value = transform_fn(root_path, self, value)
return value
Expand Down Expand Up @@ -1125,9 +1129,11 @@ def apply(
if object_utils.MISSING_VALUE == value:
value = copy.deepcopy(field.default_value)

child_path = object_utils.KeyPath(key, root_path)
new_value = field.apply(
value, allow_partial, child_transform, child_path)
value,
allow_partial=allow_partial,
transform_fn=child_transform,
root_path=object_utils.KeyPath(key, root_path))

# NOTE(daiyip): `pg.Dict.__getitem__`` has special logics in handling
# `pg.Contextual`` values. Therefore, we user `dict.__getitem__()`` to
Expand Down
Loading

0 comments on commit a83c2ee

Please sign in to comment.