Repository: mit-ll-responsible-ai/hydra-zen
Pull request #562 (2023-10-28 15:06:58)

Make `default_to_config` customizable with `BuildsFn`
This makes it easy to have ZenStore's default auto-config capabilities leverage a specialized config-creation functions.

E.g., suppose `CustomBuilds` is a subclass of `BuildsFn`, which has added auto-config support for `MyType`. Then we can create a config whose auto-config behavior leverages these added capabilities.

```python
from hydra_zen.wrapper import default_to_config
from hydra_zen import ZenStore
from functools import partial

store = ZenStore("my store")(
    to_config=partial(default_to_config, BuildsFn=CustomBuilds)
)
```

```python
>>> from hydra_zen import instantiate
>>> store(SomeType, x=1, y=SomeType(2), name='boo')
>>> instantiate(store[None, "boo"])
SomeType(1, SomeType(2, None))
```
  


Patch:
diff --git a/src/hydra_zen/wrapper/_implementations.py b/src/hydra_zen/wrapper/_implementations.py
index 92cec62d..6b20e7b7 100644
--- a/src/hydra_zen/wrapper/_implementations.py
+++ b/src/hydra_zen/wrapper/_implementations.py
@@ -44,10 +44,10 @@ from typing_extensions import (
     TypeGuard,
 )
 
-from hydra_zen import instantiate, just, make_custom_builds_fn
+from hydra_zen import instantiate
 from hydra_zen._compatibility import HYDRA_VERSION, Version
 from hydra_zen.errors import HydraZenValidationError
-from hydra_zen.structured_configs._implementations import BuildsFn
+from hydra_zen.structured_configs._implementations import BuildsFn, DefaultBuilds
 from hydra_zen.structured_configs._type_guards import safe_getattr
 from hydra_zen.typing._implementations import (
     DataClass_,
@@ -765,9 +765,6 @@ def zen(
     return wrap
 
 
-fbuilds = make_custom_builds_fn(populate_full_signature=True)
-
-
 def default_to_config(
     target: Union[
         Callable[..., Any],
@@ -777,6 +774,7 @@ def default_to_config(
         ListConfig,
         DictConfig,
     ],
+    BuildsFn: Type[BuildsFn[Any]] = DefaultBuilds,
     **kw: Any,
 ) -> Union[DataClass_, Type[DataClass_], ListConfig, DictConfig]:
     """Creates a config that describes `target`.
@@ -795,6 +793,10 @@ def default_to_config(
     ----------
     target : Callable[..., Any] | DataClass | Type[DataClass] | list | dict
 
+    BuildsFn : Type[BuildsFn[Any]], optional (default=DefaultBuilds)
+        Provides the config-creation functions (`builds`, `just`) used
+        by this function.
+
     **kw : Any
         Keyword arguments to be passed to `builds`.
 
@@ -834,6 +836,7 @@ def default_to_config(
     x: ???
     'y': ???
     """
+
     if is_dataclass(target):
         if isinstance(target, type):
             if issubclass(target, HydraConf):
@@ -843,7 +846,12 @@ def default_to_config(
             if not kw and get_obj_path(target).startswith("types."):
                 # handles dataclasses returned by make_config()
                 return target
-            return fbuilds(target, **kw, builds_bases=(target,))
+            return BuildsFn.builds(
+                target,
+                **kw,
+                populate_full_signature=True,
+                builds_bases=(target,),
+            )
         if kw:
             raise ValueError(
                 "store(<dataclass-instance>, [...]) does not support specifying "
@@ -853,12 +861,14 @@ def default_to_config(
 
     elif isinstance(target, (dict, list)):
         # TODO: convert to OmegaConf containers?
-        return just(target)
+        return BuildsFn.just(target)
     elif isinstance(target, (DictConfig, ListConfig)):
         return target
     else:
         t = cast(Callable[..., Any], target)
-        return cast(Type[DataClass_], fbuilds(t, **kw))
+        return cast(
+            Type[DataClass_], BuildsFn.builds(t, **kw, populate_full_signature=True)
+        )
 
 
 class _HasName(Protocol):


Test patch:
diff --git a/tests/test_BuildsFn.py b/tests/test_BuildsFn.py
index d58c5bdb..80fa5b3f 100644
--- a/tests/test_BuildsFn.py
+++ b/tests/test_BuildsFn.py
@@ -1,6 +1,7 @@
 # Copyright (c) 2023 Massachusetts Institute of Technology
 # SPDX-License-Identifier: MIT
 
+from functools import partial
 from inspect import signature
 from typing import Any, List, Union
 
@@ -10,6 +11,7 @@ from typing_extensions import Literal
 from hydra_zen import (
     BuildsFn,
     ZenField,
+    ZenStore,
     builds,
     instantiate,
     just,
@@ -19,10 +21,11 @@ from hydra_zen import (
 from hydra_zen.errors import HydraZenUnsupportedPrimitiveError
 from hydra_zen.typing import DataclassOptions, SupportedPrimitive
 from hydra_zen.typing._implementations import DataclassOptions
+from hydra_zen.wrapper import default_to_config
 
 
 class A:
-    def __init__(self, x: int) -> None:
+    def __init__(self, x: Any) -> None:
         self.x = x
 
     def __eq__(self, __value: object) -> bool:
@@ -165,3 +168,11 @@ def test_zen_field():
     with pytest.raises(HydraZenUnsupportedPrimitiveError):
         ZenField(default=A(1))
     ZenField(default=A(1), _builds_fn=my_builds)
+
+
+def test_default_to_config():
+    store = ZenStore("my store")(
+        to_config=partial(default_to_config, BuildsFn=MyBuildsFn)
+    )
+    store(A, x=A(x=2), name="blah")
+    assert instantiate(store[None, "blah"]) == A(x=A(x=2))