From e4af3c804e9eee25657b543c31bd3fe63137a013 Mon Sep 17 00:00:00 2001 From: Sasha Mitchell Date: Thu, 1 Oct 2026 01:09:12 +0700 Subject: [PATCH 1/3] Reject an explicit zero float width instead of the IEEE default. A name such as f32e8m0 was parsed as float32 because 0 was treated as a missing width. The same substitution turned f16e0m10 into float16. Signed-off-by: Sasha Mitchell --- .../nvidia/dali/experimental/dynamic/_type.py | 13 ++++++++++--- dali/test/python/experimental_mode/test_type.py | 13 +++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/dali/python/nvidia/dali/experimental/dynamic/_type.py b/dali/python/nvidia/dali/experimental/dynamic/_type.py index 446503379ca..3313aa4b5eb 100644 --- a/dali/python/nvidia/dali/experimental/dynamic/_type.py +++ b/dali/python/nvidia/dali/experimental/dynamic/_type.py @@ -126,8 +126,12 @@ def __init__( self.type_id = type_id if kind == DType.Kind.float: - self.exponent_bits = exponent_bits or DType._default_exponent_bits(bits, True) - self.significand_bits = significand_bits or DType._default_significand_bits(bits, True) + if exponent_bits is None: + exponent_bits = DType._default_exponent_bits(bits, True) + if significand_bits is None: + significand_bits = DType._default_significand_bits(bits, True) + self.exponent_bits = exponent_bits + self.significand_bits = significand_bits else: self.exponent_bits = None self.significand_bits = None @@ -278,7 +282,10 @@ def parse_internal(name: str) -> "DType": t = parse_internal(name) if t.type_id is None: - t.type_id = _type2id[t] + try: + t.type_id = _type2id[t] + except KeyError: + raise ValueError(f"Unsupported type name: {name}") from None if t.type_id is not None: t = _id2type[t.type_id] # use the same DType instance as the one registered with the id _name2type[name] = t diff --git a/dali/test/python/experimental_mode/test_type.py b/dali/test/python/experimental_mode/test_type.py index fb369dfc9ad..e314782fedb 100644 --- a/dali/test/python/experimental_mode/test_type.py +++ b/dali/test/python/experimental_mode/test_type.py @@ -166,3 +166,16 @@ def test_type_significand_bits(): assert ndd.float16.significand_bits == 10 assert ndd.float32.significand_bits == 23 assert ndd.float64.significand_bits == 52 + + +def test_explicit_zero_float_field_is_not_replaced_with_the_default(): + # A written 0 used to be treated as missing, so these came back as float32 and float16. + for name in ("f32e8m0", "f16e0m10"): + try: + parsed = ndd.dtype(name) + except ValueError: + continue + raise AssertionError( + f"{name} was accepted as {parsed.name} " + f"(exponent {parsed.exponent_bits}, significand {parsed.significand_bits})" + ) From 3c9da3f3290375a1405797d806b4a4281436e778 Mon Sep 17 00:00:00 2001 From: Sasha Mitchell Date: Sat, 17 Oct 2026 23:35:16 +0800 Subject: [PATCH 2/3] Drop a rejected type name from the parse cache. DType.__init__ records the temporary type before the registered-type lookup. A failed parse of f32e8m0 left that name in the cache, so the next parse returned it. The error now names the input. Signed-off-by: Sasha Mitchell --- dali/python/nvidia/dali/experimental/dynamic/_type.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dali/python/nvidia/dali/experimental/dynamic/_type.py b/dali/python/nvidia/dali/experimental/dynamic/_type.py index 3313aa4b5eb..6f6350cab70 100644 --- a/dali/python/nvidia/dali/experimental/dynamic/_type.py +++ b/dali/python/nvidia/dali/experimental/dynamic/_type.py @@ -285,7 +285,16 @@ def parse_internal(name: str) -> "DType": try: t.type_id = _type2id[t] except KeyError: - raise ValueError(f"Unsupported type name: {name}") from None + # __init__ caches the temporary type before this lookup. Drop that + # entry so a later parse does not return the rejected type. Leave a + # name that already pointed at a different type alone. + if _name2type.get(t.name) is t: + del _name2type[t.name] + if name != t.name and _name2type.get(name) is t: + del _name2type[name] + raise ValueError( + f"{name} is not a registered type. Expected a name such as f16 or f32." + ) from None if t.type_id is not None: t = _id2type[t.type_id] # use the same DType instance as the one registered with the id _name2type[name] = t From 4ebf21f9ce462040d8d0a29bf73c356ac33c2823 Mon Sep 17 00:00:00 2001 From: Sasha Mitchell Date: Sat, 7 Nov 2026 23:58:29 +0700 Subject: [PATCH 3/3] Reject a float width below 1. A written 0 is an invalid exponent or significand, not a missing width. An unregistered name no longer suggests f16 or f32. --- dali/python/nvidia/dali/experimental/dynamic/_type.py | 8 +++++--- dali/test/python/experimental_mode/test_type.py | 10 ++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/dali/python/nvidia/dali/experimental/dynamic/_type.py b/dali/python/nvidia/dali/experimental/dynamic/_type.py index 6f6350cab70..ee06fbd5625 100644 --- a/dali/python/nvidia/dali/experimental/dynamic/_type.py +++ b/dali/python/nvidia/dali/experimental/dynamic/_type.py @@ -128,8 +128,12 @@ def __init__( if kind == DType.Kind.float: if exponent_bits is None: exponent_bits = DType._default_exponent_bits(bits, True) + elif exponent_bits < 1: + raise ValueError(f"Invalid number of exponent bits: {exponent_bits}") if significand_bits is None: significand_bits = DType._default_significand_bits(bits, True) + elif significand_bits < 1: + raise ValueError(f"Invalid number of significand bits: {significand_bits}") self.exponent_bits = exponent_bits self.significand_bits = significand_bits else: @@ -292,9 +296,7 @@ def parse_internal(name: str) -> "DType": del _name2type[t.name] if name != t.name and _name2type.get(name) is t: del _name2type[name] - raise ValueError( - f"{name} is not a registered type. Expected a name such as f16 or f32." - ) from None + raise ValueError(f"{name} is not a registered type.") from None if t.type_id is not None: t = _id2type[t.type_id] # use the same DType instance as the one registered with the id _name2type[name] = t diff --git a/dali/test/python/experimental_mode/test_type.py b/dali/test/python/experimental_mode/test_type.py index e314782fedb..6c3f88239bb 100644 --- a/dali/test/python/experimental_mode/test_type.py +++ b/dali/test/python/experimental_mode/test_type.py @@ -170,10 +170,16 @@ def test_type_significand_bits(): def test_explicit_zero_float_field_is_not_replaced_with_the_default(): # A written 0 used to be treated as missing, so these came back as float32 and float16. - for name in ("f32e8m0", "f16e0m10"): + cases = { + "f32e8m0": "Invalid number of significand bits: 0", + "f16e0m10": "Invalid number of exponent bits: 0", + } + for name, message in cases.items(): try: parsed = ndd.dtype(name) - except ValueError: + except ValueError as err: + if message not in str(err): + raise AssertionError(f"{name} raised {err!r}, want {message}") from err continue raise AssertionError( f"{name} was accepted as {parsed.name} "