cattrs icon indicating copy to clipboard operation
cattrs copied to clipboard

When structuring, how to forbid type casting to base classes?

Open philip-bl opened this issue 1 year ago • 2 comments

I want to use cattrs to structure dicts and other stuff to attrs. However, I am unhappy with the default behaviour that if attrs expects an int, float, str, bool or something like that, it casts the object to that type using int(), float(), str(), or bool() respectively. I want cattrs to structure containers such as sequences, dicts, etc. but I don't want it to typecast base types - I want it to error out instead if the types don't match. How can I do this?

philip-bl avatar Dec 12 '23 10:12 philip-bl

Hello!

You can override the default hooks for those types to be anything you like. Cattrs is based on function composition to this is how problems are usually solved.

A simple solution would be like this:

from cattrs import Converter

c = Converter()


def structure_int(val, _) -> int:
    if not isinstance(val, int):
        raise ValueError("Not an int")
    return val


c.register_structure_hook(int, structure_int)

c.structure("1", int)  # Error

You can get fancier by making use of the fact structure hooks receive their target type as their second argument, like this:

c = Converter()


def validate_type(val, type):
    if not isinstance(val, type):
        raise ValueError(f"Not a {type}")
    return val


c.register_structure_hook(int, validate_type)
c.register_structure_hook(float, validate_type)
c.register_structure_hook(str, validate_type)
c.register_structure_hook(bool, validate_type)

c.structure("1", bool)

These rules propagate to collections, attrs classes etc.

Tinche avatar Dec 12 '23 12:12 Tinche

I'm planning a strategy to make this easier, probably in the next version.

Tinche avatar Dec 12 '23 12:12 Tinche