Skip to content

Commit 75479c2

Browse files
miaout17copybara-github
authored andcommitted
Fix __qualname__ AttributeError in fiddle config.py
If a partial function generated by `functools.partial` is passed into fiddle AND certain exceptions are raised, accessing `__qualname__` will trigger another exception, prevent the users from seeing the real error messages. Type type `TypeOrCallableProducingT = Union[Callable[..., T], Type[T]]` does not guarantee that `__qualname__` will exist. - Added a regression test case in third_party/py/fiddle/_src/config_test.py that uses functools.partial to trigger the bug. - Replace all `self.__fn_or_cls__.__qualname__` with the existing `self._fn_or_cls_name_repr()` which already solved the problem. PiperOrigin-RevId: 893119387
1 parent bf94959 commit 75479c2

2 files changed

Lines changed: 16 additions & 3 deletions

File tree

fiddle/_src/config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,22 +348,22 @@ def __getattr__(self, name: str):
348348
) and _field_uses_default_factory(self.__fn_or_cls__, name):
349349
raise ValueError(
350350
"Can't get default value for dataclass field "
351-
+ f'{self.__fn_or_cls__.__qualname__}.{name} '
351+
+ f'{self._fn_or_cls_name_repr()}.{name} '
352352
+ 'since it uses a default_factory.'
353353
)
354354
if param is not None and param.default is not param.empty:
355355
return param.default
356356
msg = (
357357
f"No parameter '{name}' has been set on {self!r} "
358-
f"(Buildable='{self.__fn_or_cls__.__qualname__}', missing "
358+
f"(Buildable='{self._fn_or_cls_name_repr()}', missing "
359359
f"parameter='{name}')."
360360
)
361361
# TODO(b/219988937): Implement an edit distance function and display valid
362362
# attributes that are close to `name`.
363363
if hasattr(self.__fn_or_cls__, name):
364364
msg += (
365365
f' Note: {self.__fn_or_cls__.__module__}.'
366-
f'{self.__fn_or_cls__.__qualname__} has an attribute/method with '
366+
f'{self._fn_or_cls_name_repr()} has an attribute/method with '
367367
'this name, so this could be caused by using a '
368368
f'fdl.{self.__class__.__qualname__} in '
369369
'place of the actual function or class being configured. Did you '

fiddle/_src/config_test.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import copy
1919
import dataclasses
20+
import functools
2021
import pickle
2122
import sys
2223
import threading
@@ -851,6 +852,18 @@ def test_nonexistent_attribute_error(self):
851852
with self.assertRaisesRegex(AttributeError, expected_msg):
852853
getattr(class_config, 'nonexistent_arg')
853854

855+
def test_partial_nonexistent_attribute_error(self):
856+
# This test serves as a regression test that `functools.partial`
857+
# wasn't well handled in error reporting code.
858+
partial_fn = functools.partial(basic_fn, arg1=1)
859+
cfg = fdl.Config(partial_fn)
860+
# The expected error should indicate the missing parameter, not crash trying
861+
# to access __qualname__ on the partial object.
862+
with self.assertRaisesRegex(
863+
AttributeError, r"No parameter 'nonexistent_arg' has been set"
864+
):
865+
getattr(cfg, 'nonexistent_arg')
866+
854867
def test_nonexistent_parameter_error(self):
855868
class_config = fdl.Config(SampleClass)
856869
expected_msg = (r"No parameter named 'nonexistent_arg' exists for "

0 commit comments

Comments
 (0)