pycrdt icon indicating copy to clipboard operation
pycrdt copied to clipboard

getattr with default value can raise an error

Open Faraphel opened this issue 2 months ago • 7 comments

When trying to use getattr with a default argument on any object inheriting from Typed, a KeyError will be raised instead of returning the default value.

_check_key, _map.py:267
__getitem__, _map.py:156
__getattr__, _base.py:380

This come from the function Map._check_key that will raise a KeyError if a key is not present in the map. It is called by its own __getitem__, used by TypedMap.__getattr__.

The issue is that for getattr to use the default value, an AttributeError must be raised instead. This can be easily fixed by rethrowing the error in Typed.__getattr__ with the correct type.


Original function :

        def __getattr__(self, key: str) -> Any:
            annotations = self.__dict__["annotations"]
            if key not in annotations:
                raise AttributeError(f'"{type(self).mro()[0]}" has no attribute "{key}"')
            expected_type = annotations[key]
            if hasattr(expected_type, "mro") and Typed in expected_type.mro():
                return expected_type(self._[key])
            return self._[key]

Updated function :

        def __getattr__(self, key: str) -> Any:
            annotations = self.__dict__["annotations"]
            if key not in annotations:
                raise AttributeError(f'"{type(self).mro()[0]}" has no attribute "{key}"')
            expected_type = annotations[key]
            if hasattr(expected_type, "mro") and Typed in expected_type.mro():
                try:
                    return expected_type(self._[key])
                except KeyError:
                    raise AttributeError(f'"{type(self).mro()[0]}" has no attribute "{key}"')
            return self._[key]

Faraphel avatar Oct 09 '25 10:10 Faraphel