From 9dc968ea226fbaed6b6c137829fa76c0b17bf41a Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Wed, 15 Nov 2023 11:22:57 -0500 Subject: [PATCH 1/7] fix store autoconfig issue --- .../structured_configs/_implementations.py | 4 +++ src/hydra_zen/wrapper/_implementations.py | 32 ++++++++++--------- tests/test_store.py | 9 ++++++ 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/hydra_zen/structured_configs/_implementations.py b/src/hydra_zen/structured_configs/_implementations.py index 9217ad619..f8919c2c5 100644 --- a/src/hydra_zen/structured_configs/_implementations.py +++ b/src/hydra_zen/structured_configs/_implementations.py @@ -175,6 +175,10 @@ del _builtin_function_or_method_type +class _Null: + ... + + def _retain_type_info(type_: type, value: Any, hydra_recursive: Optional[bool]): # OmegaConf's type-checking occurs before instantiation occurs. # This means that, e.g., passing `Builds[int]` to a field `x: int` diff --git a/src/hydra_zen/wrapper/_implementations.py b/src/hydra_zen/wrapper/_implementations.py index 63fa6d1fe..e94069262 100644 --- a/src/hydra_zen/wrapper/_implementations.py +++ b/src/hydra_zen/wrapper/_implementations.py @@ -9,6 +9,7 @@ from functools import partial, wraps from inspect import Parameter, iscoroutinefunction, signature from typing import ( + TYPE_CHECKING, Any, Callable, DefaultDict, @@ -48,7 +49,7 @@ 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, DefaultBuilds +from hydra_zen.structured_configs._implementations import DefaultBuilds from hydra_zen.structured_configs._type_guards import safe_getattr from hydra_zen.typing._implementations import ( DataClass_, @@ -61,9 +62,12 @@ from ..structured_configs._type_guards import is_dataclass from ..structured_configs._utils import safe_name +if TYPE_CHECKING: + from hydra_zen import BuildsFn + + __all__ = ["zen", "store", "Zen"] -get_obj_path = BuildsFn._get_obj_path # type: ignore R = TypeVar("R") P = ParamSpec("P") @@ -830,7 +834,7 @@ def default_to_config( ListConfig, DictConfig, ], - BuildsFn: Type[BuildsFn[Any]] = DefaultBuilds, + CustomBuildsFn: Type["BuildsFn[Any]"] = DefaultBuilds, **kw: Any, ) -> Union[DataClass_, Type[DataClass_], ListConfig, DictConfig]: """Creates a config that describes `target`. @@ -849,7 +853,7 @@ def default_to_config( ---------- target : Callable[..., Any] | DataClass | Type[DataClass] | list | dict - BuildsFn : Type[BuildsFn[Any]], optional (default=DefaultBuilds) + CustomBuildsFn : Type[BuildsFn[Any]], optional (default=DefaultBuilds) Provides the config-creation functions (`builds`, `just`) used by this function. @@ -893,21 +897,20 @@ def default_to_config( 'y': ??? """ + kw = kw.copy() + if is_dataclass(target): if isinstance(target, type): if issubclass(target, HydraConf): # don't auto-config HydraConf return target - if not kw and get_obj_path(target).startswith("types."): + if not kw and CustomBuildsFn._get_obj_path(target).startswith("types."): # type: ignore # handles dataclasses returned by make_config() return target - return BuildsFn.builds( - target, - **kw, - populate_full_signature=True, - builds_bases=(target,), - ) + kw.setdefault("populate_full_signature", True) + kw.setdefault("builds_bases", (target,)) + return CustomBuildsFn.builds(target, **kw) if kw: raise ValueError( "store(, [...]) does not support specifying " @@ -917,14 +920,13 @@ def default_to_config( elif isinstance(target, (dict, list)): # TODO: convert to OmegaConf containers? - return BuildsFn.just(target) + return CustomBuildsFn.just(target) elif isinstance(target, (DictConfig, ListConfig)): return target else: t = cast(Callable[..., Any], target) - return cast( - Type[DataClass_], BuildsFn.builds(t, **kw, populate_full_signature=True) - ) + kw.setdefault("populate_full_signature", True) + return cast(Type[DataClass_], CustomBuildsFn.builds(t, **kw)) class _HasName(Protocol): diff --git a/tests/test_store.py b/tests/test_store.py index 995428744..1bb31d6f6 100644 --- a/tests/test_store.py +++ b/tests/test_store.py @@ -24,6 +24,7 @@ just, make_config, store as default_store, + to_yaml, ) from tests.custom_strategies import new_stores, store_entries @@ -982,3 +983,11 @@ def test_merge(): assert s3._queue == {(None, "a"), (None, "b"), (None, "c")} assert s3._internal_repo[None, "a"] is not s1._internal_repo[None, "a"] assert s3._internal_repo[None, "b"] is not s2._internal_repo[None, "b"] + + +def test_disable_pop_sig_autoconfig(): + s = ZenStore() + s(ZenStore, populate_full_signature=False, name="s") + assert len(to_yaml(s).splitlines()) == 1 + sout = instantiate(s[None, "s"]) + assert isinstance(sout, ZenStore) From 847ac204f749ee94c1231275d45033ea49b9fdbe Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Wed, 15 Nov 2023 11:33:23 -0500 Subject: [PATCH 2/7] fix test --- src/hydra_zen/structured_configs/_implementations.py | 12 ++++++++---- tests/test_BuildsFn.py | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/hydra_zen/structured_configs/_implementations.py b/src/hydra_zen/structured_configs/_implementations.py index f8919c2c5..de979c479 100644 --- a/src/hydra_zen/structured_configs/_implementations.py +++ b/src/hydra_zen/structured_configs/_implementations.py @@ -564,6 +564,10 @@ def _check_instance(*target_types: str, value: "Any", module: str): # pragma: n _check_instance, "required", module="torch.optim.optimizer" ) +_is_pydantic_BaseModel = functools.partial( + _check_instance, "BaseModel", module="pydantic" +) + def _check_for_dynamically_defined_dataclass_type(target_path: str, value: Any) -> None: if target_path.startswith("types."): @@ -1100,7 +1104,7 @@ def _make_hydra_compatible( pydantic = sys.modules.get("pydantic") if pydantic is not None: # pragma: no cover - if isinstance(value, pydantic.fields.FieldInfo): + if _check_instance("FieldInfo", module="pydantic.fields", value=value): _val = ( value.default_factory() # type: ignore if value.default_factory is not None # type: ignore @@ -1119,11 +1123,11 @@ def _make_hydra_compatible( hydra_convert=hydra_convert, hydra_recursive=hydra_recursive, ) - if isinstance(value, pydantic.BaseModel): + if _is_pydantic_BaseModel(value=value): return cls.builds(type(value), **value.__dict__) - if isinstance(value, str) or ( - pydantic is not None and isinstance(value, pydantic.AnyUrl) + if isinstance(value, str) or _check_instance( + "AnyUrl", module="pydantic", value=value ): # Supports pydantic.AnyURL _v = str(value) diff --git a/tests/test_BuildsFn.py b/tests/test_BuildsFn.py index d6e773436..c418dcbb7 100644 --- a/tests/test_BuildsFn.py +++ b/tests/test_BuildsFn.py @@ -167,7 +167,7 @@ def test_zen_field(): def test_default_to_config(): store = ZenStore("my store")( - to_config=partial(default_to_config, BuildsFn=MyBuildsFn) + to_config=partial(default_to_config, CustomBuildsFn=MyBuildsFn) ) store(A, x=A(x=2), name="blah") assert instantiate(store[None, "blah"]) == A(x=A(x=2)) From 958ff590137bd1ab33e621ea6a9178adcc4af1a5 Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Wed, 15 Nov 2023 11:41:31 -0500 Subject: [PATCH 3/7] fix test --- tests/test_store.py | 3 ++- tests/test_third_party/test_using_v1_pydantic.py | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/test_store.py b/tests/test_store.py index 1bb31d6f6..31d0e2e5c 100644 --- a/tests/test_store.py +++ b/tests/test_store.py @@ -988,6 +988,7 @@ def test_merge(): def test_disable_pop_sig_autoconfig(): s = ZenStore() s(ZenStore, populate_full_signature=False, name="s") - assert len(to_yaml(s).splitlines()) == 1 + config = s[None, "s"] + assert len(to_yaml(config).splitlines()) == 1 sout = instantiate(s[None, "s"]) assert isinstance(sout, ZenStore) diff --git a/tests/test_third_party/test_using_v1_pydantic.py b/tests/test_third_party/test_using_v1_pydantic.py index ed0a10d59..e28070425 100644 --- a/tests/test_third_party/test_using_v1_pydantic.py +++ b/tests/test_third_party/test_using_v1_pydantic.py @@ -1,6 +1,7 @@ # Copyright (c) 2023 Massachusetts Institute of Technology # SPDX-License-Identifier: MIT import dataclasses +import sys from typing import Any, List, Optional import hypothesis.strategies as st @@ -28,6 +29,12 @@ ) +def test_BaseModel(): + _pydantic = sys.modules.get("pydantic") + assert _pydantic is not None + assert _pydantic.BaseModel is BaseModel + + @parametrize_pydantic_fields def test_pydantic_specific_fields_function(custom_type, good_val, bad_val): def f(x): From af88e278e3bd80534ad9c924e690cf14ed05d24a Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Wed, 15 Nov 2023 11:45:32 -0500 Subject: [PATCH 4/7] remove unused code --- src/hydra_zen/structured_configs/_implementations.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/hydra_zen/structured_configs/_implementations.py b/src/hydra_zen/structured_configs/_implementations.py index de979c479..74d7fbda5 100644 --- a/src/hydra_zen/structured_configs/_implementations.py +++ b/src/hydra_zen/structured_configs/_implementations.py @@ -175,10 +175,6 @@ del _builtin_function_or_method_type -class _Null: - ... - - def _retain_type_info(type_: type, value: Any, hydra_recursive: Optional[bool]): # OmegaConf's type-checking occurs before instantiation occurs. # This means that, e.g., passing `Builds[int]` to a field `x: int` From 183af9d4a52607c4b2d8a4054369b03fbae846d6 Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Wed, 15 Nov 2023 11:49:13 -0500 Subject: [PATCH 5/7] strengthen test --- tests/test_store.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_store.py b/tests/test_store.py index 31d0e2e5c..7ddebd4ee 100644 --- a/tests/test_store.py +++ b/tests/test_store.py @@ -992,3 +992,10 @@ def test_disable_pop_sig_autoconfig(): assert len(to_yaml(config).splitlines()) == 1 sout = instantiate(s[None, "s"]) assert isinstance(sout, ZenStore) + + s2 = ZenStore() + s2(ZenStore, name="s") + config2 = s2[None, "s"] + assert len(to_yaml(config2).splitlines()) > 1 + sout2 = instantiate(s2[None, "s"]) + assert isinstance(sout2, ZenStore) From a95ee303db22edd6db3e9926050f514eae7d8c6f Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Wed, 15 Nov 2023 11:53:57 -0500 Subject: [PATCH 6/7] straggler --- src/hydra_zen/structured_configs/_implementations.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/hydra_zen/structured_configs/_implementations.py b/src/hydra_zen/structured_configs/_implementations.py index 74d7fbda5..3e3b35d15 100644 --- a/src/hydra_zen/structured_configs/_implementations.py +++ b/src/hydra_zen/structured_configs/_implementations.py @@ -1106,7 +1106,10 @@ def _make_hydra_compatible( if value.default_factory is not None # type: ignore else value.default # type: ignore ) - if isinstance(_val, pydantic.fields.UndefinedType): + + if _check_instance( + "UndefinedType", module="pydantic.fields", value=value + ): return MISSING return cls._make_hydra_compatible( From 3985e357e916c523bea69eb50cdcb33957b6ec91 Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Wed, 15 Nov 2023 12:04:01 -0500 Subject: [PATCH 7/7] fix --- src/hydra_zen/structured_configs/_implementations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hydra_zen/structured_configs/_implementations.py b/src/hydra_zen/structured_configs/_implementations.py index 3e3b35d15..0d98e6f47 100644 --- a/src/hydra_zen/structured_configs/_implementations.py +++ b/src/hydra_zen/structured_configs/_implementations.py @@ -1108,7 +1108,7 @@ def _make_hydra_compatible( ) if _check_instance( - "UndefinedType", module="pydantic.fields", value=value + "UndefinedType", module="pydantic.fields", value=_val ): return MISSING