Repository: mit-ll-responsible-ai/hydra-zen
Pull request #267 (2022-04-22 21:37:03)

Fix overly-permissive annotation: nested tuples
#261 enabled annotations for nested containers for omegaconf > 2.2.0. It incorrectly permitted nested tuples. This PR fixes that.


Patch:
diff --git a/src/hydra_zen/structured_configs/_utils.py b/src/hydra_zen/structured_configs/_utils.py
index 5f721dc6..634e6311 100644
--- a/src/hydra_zen/structured_configs/_utils.py
+++ b/src/hydra_zen/structured_configs/_utils.py
@@ -267,7 +267,11 @@ NoneType = type(None)
 
 
 def sanitized_type(
-    type_: Any, *, primitive_only: bool = False, wrap_optional: bool = False
+    type_: Any,
+    *,
+    primitive_only: bool = False,
+    wrap_optional: bool = False,
+    nested: bool = False,
 ) -> type:
     """Returns ``type_`` unchanged if it is supported as an annotation by hydra,
     otherwise returns ``Any``.
@@ -322,19 +326,19 @@ def sanitized_type(
             return Union[optional_type, NoneType]  # type: ignore
 
         if origin is list or origin is List:
-            return List[sanitized_type(args[0], primitive_only=no_nested_container)] if args else type_  # type: ignore
+            return List[sanitized_type(args[0], primitive_only=no_nested_container, nested=True)] if args else type_  # type: ignore
 
         if origin is dict or origin is Dict:
             return (
                 Dict[
-                    sanitized_type(args[0], primitive_only=True),  # type: ignore
-                    sanitized_type(args[1], primitive_only=no_nested_container),  # type: ignore
+                    sanitized_type(args[0], primitive_only=True, nested=True),  # type: ignore
+                    sanitized_type(args[1], primitive_only=no_nested_container, nested=True),  # type: ignore
                 ]
                 if args
                 else type_
             )
 
-        if origin is tuple or origin is Tuple:
+        if (origin is tuple or origin is Tuple) and not nested:
             # hydra silently supports tuples of homogenous types
             # It has some weird behavior. It treats `Tuple[t1, t2, ...]` as `List[t1]`
             # It isn't clear that we want to perpetrate this on our end..
@@ -348,7 +352,7 @@ def sanitized_type(
             has_ellipses = Ellipsis in unique_args
 
             _unique_type = (
-                sanitized_type(args[0], primitive_only=no_nested_container)
+                sanitized_type(args[0], primitive_only=no_nested_container, nested=True)
                 if len(unique_args) == 1 or (len(unique_args) == 2 and has_ellipses)
                 else Any
             )


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 63b497ae..b4617460 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -157,6 +157,7 @@ NoneType = type(None)
             List[List[int]],
             List[Any] if not HYDRA_SUPPORTS_NESTED_CONTAINER_TYPES else List[List[int]],
         ),
+        (List[Tuple[int, int]], List[Any]),
         (List[T], List[Any]),
         (Dict[str, float], Dict[str, float]),
         (Dict[C, int], Dict[Any, int]),
@@ -191,6 +192,8 @@ NoneType = type(None)
             List[List[Type[int]]],
             List[Any] if not HYDRA_SUPPORTS_NESTED_CONTAINER_TYPES else List[List[Any]],
         ),
+        (Tuple[Tuple[int, ...], ...], Tuple[Any, ...]),
+        (Optional[Tuple[Tuple[int, ...], ...]], Optional[Tuple[Any, ...]]),
     ],
 )
 def test_sanitized_type_expected_behavior(in_type, expected_type):