Repository: mit-ll-responsible-ai/hydra-zen

Issue:
omegaconf is adding native support for `bytes` 
Tracking at https://github.com/omry/omegaconf/pull/845

On our end: 
- [x] `bytes` needs to be (dynamically) added to `HYDRA_SUPPORTED_PRIMITIVE_TYPES` for `omegaconf >= 2.2.0` and removed  from `ZEN_SUPPORTED_PRIMITIVES`.
- [x] Ensure that `bytes`, used as a type-annotation, is not broadened to `Any`
- [x] Update [our docs](https://mit-ll-responsible-ai.github.io/hydra-zen/api_reference.html#additional-types-supported-via-hydra-zen)  to reflect change in behavior.

Discussion:

Patch:
diff --git a/docs/source/api_reference.rst b/docs/source/api_reference.rst
index b7603dcf..2132a8dd 100644
--- a/docs/source/api_reference.rst
+++ b/docs/source/api_reference.rst
@@ -116,7 +116,7 @@ Values of the following types can be specified directly in configs:
 - :py:class:`list`
 - :py:class:`dict`
 - :py:class:`enum.Enum`
-
+- :py:class:`bytes`  (*support added in OmegaConf 2.2.0*)
 
 .. _additional-types:
 
@@ -147,12 +147,12 @@ with Hydra. For example, a :py:class:`complex` value can be specified directly v
 
 hydra-zen provides specialized support for values of the following types:
 
-- :py:class:`bytes`
+- :py:class:`bytes` (*support provided for OmegaConf < 2.2.0*)
 - :py:class:`bytearray`
 - :py:class:`complex`
 - :py:class:`collections.Counter`
 - :py:class:`collections.deque`
-- :py:func:`functools.partial`  (*added in v0.5.0*)
+- :py:func:`functools.partial`
 - :py:class:`pathlib.Path`
 - :py:class:`pathlib.PosixPath`
 - :py:class:`pathlib.WindowsPath`
diff --git a/src/hydra_zen/_compatibility.py b/src/hydra_zen/_compatibility.py
index d5b3f21d..fd6d8b9d 100644
--- a/src/hydra_zen/_compatibility.py
+++ b/src/hydra_zen/_compatibility.py
@@ -52,6 +52,7 @@ PATCH_OMEGACONF_830: Final = True  # OMEGACONF_VERSION < Version(2, 1, 1)
 HYDRA_SUPPORTS_PARTIAL: Final = Version(1, 1, 1) < HYDRA_VERSION
 
 HYDRA_SUPPORTS_NESTED_CONTAINER_TYPES: Final = OMEGACONF_VERSION >= Version(2, 2, 0)
+HYDRA_SUPPORTS_BYTES: Final = OMEGACONF_VERSION >= Version(2, 2, 0)
 
 # Indicates primitive types permitted in type-hints of structured configs
 HYDRA_SUPPORTED_PRIMITIVE_TYPES: Final = {int, float, bool, str, Enum}
@@ -65,9 +66,15 @@ ZEN_SUPPORTED_PRIMITIVES: Set[type] = {
     Path,
     PosixPath,
     WindowsPath,
-    bytes,
     bytearray,
     deque,
     Counter,
     range,
 }
+
+
+if HYDRA_SUPPORTS_BYTES:  # pragma: no cover
+    HYDRA_SUPPORTED_PRIMITIVES.add(bytes)
+    HYDRA_SUPPORTED_PRIMITIVE_TYPES.add(bytes)
+else:  # pragma: no cover
+    ZEN_SUPPORTED_PRIMITIVES.add(bytes)
diff --git a/src/hydra_zen/structured_configs/_implementations.py b/src/hydra_zen/structured_configs/_implementations.py
index 9b0f6e03..b59b6761 100644
--- a/src/hydra_zen/structured_configs/_implementations.py
+++ b/src/hydra_zen/structured_configs/_implementations.py
@@ -1802,7 +1802,10 @@ def _unpack_partial(value: Partial[_T]) -> PartialBuilds[Type[_T]]:
 ZEN_VALUE_CONVERSION[set] = _cast_via_tuple(set)
 ZEN_VALUE_CONVERSION[frozenset] = _cast_via_tuple(frozenset)
 ZEN_VALUE_CONVERSION[deque] = _cast_via_tuple(deque)
-ZEN_VALUE_CONVERSION[bytes] = _cast_via_tuple(bytes)
+
+if bytes in ZEN_SUPPORTED_PRIMITIVES:  # pragma: no cover
+    ZEN_VALUE_CONVERSION[bytes] = _cast_via_tuple(bytes)
+
 ZEN_VALUE_CONVERSION[bytearray] = _cast_via_tuple(bytearray)
 ZEN_VALUE_CONVERSION[range] = lambda value: builds(
     range, value.start, value.stop, value.step
diff --git a/src/hydra_zen/typing/_implementations.py b/src/hydra_zen/typing/_implementations.py
index cdde221a..4296dcca 100644
--- a/src/hydra_zen/typing/_implementations.py
+++ b/src/hydra_zen/typing/_implementations.py
@@ -190,13 +190,13 @@ _HydraPrimitive: TypeAlias = Union[
     int,
     float,
     str,
+    ByteString,
 ]
 
 _SupportedViaBuilds = Union[
     Partial[Any],
     range,
     Set[Any],
-    ByteString,
 ]
 
 _SupportedPrimitive: TypeAlias = Union[


Test patch:
diff --git a/tests/annotations/declarations.py b/tests/annotations/declarations.py
index ef4447d2..1ff16c3b 100644
--- a/tests/annotations/declarations.py
+++ b/tests/annotations/declarations.py
@@ -139,11 +139,11 @@ def check_just():
 
     reveal_type(just(1), expected_text="int")
     reveal_type(just("hi"), expected_text="str")
+    reveal_type(just(b"1234"), expected_text="bytes")
     reveal_type(just(1 + 2j), expected_text="ConfigComplex")
     reveal_type(just(Path.home()), expected_text="ConfigPath")
     reveal_type(just(partial(f, 1)), expected_text="Type[Just[partial[int]]]")
     reveal_type(just(set([1, 2, 3])), expected_text="Builds[Type[set[int]]]")
-    reveal_type(just(b"1234"), expected_text="Builds[Type[bytes]]")
     reveal_type(just(range(10)), expected_text="Builds[Type[range]]")
 
     partiald_f = instantiate(just(partial(f, 1)))
diff --git a/tests/test_compatibility/test_primitive_support.py b/tests/test_compatibility/test_primitive_support.py
new file mode 100644
index 00000000..91156ea9
--- /dev/null
+++ b/tests/test_compatibility/test_primitive_support.py
@@ -0,0 +1,27 @@
+# Copyright (c) 2022 Massachusetts Institute of Technology
+# SPDX-License-Identifier: MIT
+
+
+from dataclasses import dataclass
+
+import pytest
+from omegaconf import OmegaConf, ValidationError
+
+from hydra_zen import builds, instantiate
+from hydra_zen._compatibility import HYDRA_SUPPORTS_BYTES
+
+
+@dataclass
+class C:
+    x: bytes = b"123"
+
+
+def test_hydra_supports_bytes():
+
+    if HYDRA_SUPPORTS_BYTES:
+        OmegaConf.create(C)
+    else:
+        with pytest.raises(ValidationError):
+            OmegaConf.create(C)
+
+    assert instantiate(builds(C, populate_full_signature=True)).x == C.x
diff --git a/tests/test_just.py b/tests/test_just.py
index 5f38a771..1a3dc699 100644
--- a/tests/test_just.py
+++ b/tests/test_just.py
@@ -1,3 +1,6 @@
+# Copyright (c) 2022 Massachusetts Institute of Technology
+# SPDX-License-Identifier: MIT
+
 import functools
 from pathlib import Path
 
@@ -26,7 +29,6 @@ def func_with_cache(x: int):
 @pytest.mark.parametrize(
     "obj",
     [
-        b"123",
         bytearray([1, 2, 3]),
         1 + 2j,
         Path.cwd(),
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 48e66f72..63b497ae 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -22,6 +22,7 @@ from typing_extensions import Final, Literal
 
 from hydra_zen import builds, instantiate, mutable_value
 from hydra_zen._compatibility import (
+    HYDRA_SUPPORTS_BYTES,
     HYDRA_SUPPORTS_NESTED_CONTAINER_TYPES,
     Version,
     _get_version,
@@ -115,6 +116,7 @@ NoneType = type(None)
         (float, float),
         (str, str),
         (bool, bool),
+        (bytes, bytes if HYDRA_SUPPORTS_BYTES else Any),
         (Color, Color),
         (C, Any),  # unsupported primitives
         (type(None), Any),
diff --git a/tests/test_value_conversion.py b/tests/test_value_conversion.py
index f0017724..a34c8001 100644
--- a/tests/test_value_conversion.py
+++ b/tests/test_value_conversion.py
@@ -2,6 +2,7 @@
 # SPDX-License-Identifier: MIT
 import string
 from collections import Counter, deque
+from dataclasses import dataclass
 from enum import Enum
 from functools import partial
 from pathlib import Path
@@ -10,9 +11,16 @@ from typing import Dict, FrozenSet, List, Set, Union
 import hypothesis.strategies as st
 import pytest
 from hypothesis import HealthCheck, assume, given, settings
-from omegaconf import DictConfig, ListConfig, OmegaConf
-
-from hydra_zen import builds, get_target, instantiate, make_config, to_yaml
+from omegaconf import DictConfig, ListConfig, OmegaConf, ValidationError
+
+from hydra_zen import (
+    builds,
+    get_target,
+    instantiate,
+    make_config,
+    mutable_value,
+    to_yaml,
+)
 from hydra_zen._compatibility import ZEN_SUPPORTED_PRIMITIVES
 from hydra_zen.structured_configs._value_conversion import ZEN_VALUE_CONVERSION
 
@@ -213,3 +221,20 @@ def test_builds_handles_recursive_partial():
     assert get_target(Conf) is f1
     assert instantiate(Conf) == partiald_obj()
     assert partiald_obj() == (("a", "b"), dict(a=1, b=2))
+
+
+@given(type_=st.sampled_from(list(ZEN_SUPPORTED_PRIMITIVES)), data=st.data())
+def test_zen_supported_primitives_arent_supported_by_hydra(type_, data: st.DataObject):
+    try:
+        value = data.draw(st.from_type(type_))
+    except (TypeError, NotImplementedError):
+        assume(False)
+        return
+
+    @dataclass
+    class C:
+        x: type_ = mutable_value(value) if isinstance(value, (set, dict)) else value
+
+    with pytest.raises((ValidationError, AssertionError)):
+        Conf = OmegaConf.create(C)
+        assert isinstance(Conf.x, type_)