Skip to content

Guidance on exception handling for validators? #1143

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
poneill opened this issue Jun 7, 2023 · 2 comments
Closed

Guidance on exception handling for validators? #1143

poneill opened this issue Jun 7, 2023 · 2 comments

Comments

@poneill
Copy link

poneill commented Jun 7, 2023

Hi, I had a question about best practices for handling exceptions with attr validators.

Suppose I wanted to represent a float in the unit interval with runtime validation. The natural way to do that, afaict, would be:

import attr

@attr.s
class UnitFloat:
    """Represent a float in the unit interval."""

    x: float = attr.field(
        validator=[
            attr.validators.instance_of(float),
            attr.validators.ge(0.0),
            attr.validators.le(1.0),
        ],
    )

If I call with:

UnitFloat(1.1) I get: ValueError: 'x' must be <= 1.0: 1.1, which makes sense.

If I call with UnitFloat(1), however, I get: TypeError: ("'x' must be <class 'float'> (got 1 that is a <class 'int'>)..."

This is in accordance with the python docs (https://docs.python.org/3/library/exceptions.html) but requires the user to guard against two kinds of exceptions, depending on how exactly the constructor choked on bad input. This means that code of the form:

try:
    x = UnitFloat(unsanitized_input)
except ValueError:
    print("validation error safely caught!")

is unsafe. Is there an established convention for this pattern, like maybe:

AttrValidationError = (TypeError, ValueError)?

(There is also the option of adding a converter to each field, which would trivially reduce all bad input to ValueError, but this is probably not always desired behavior.)

Thanks!

(attrs version: '23.1.0')

@hynek
Copy link
Member

hynek commented Jun 13, 2023

This is in accordance with the python docs (https://docs.python.org/3/library/exceptions.html) but requires the user to guard against two kinds of exceptions, depending on how exactly the constructor choked on bad input.

I think this is very much on purpose, such that the user can differentiate between what went wrong.


Generally speaking, attrs doesn't try to be a validation / structuring package (we've got cattrs for that!) but a toolkit to build classes that fit into the Python eco system.

IOW a class that is built using attrs should behave as if someone has written it by hand, adhering to Python standards.

All this to say: there's no attrs-specific guidance to be had, because we're trying to be good Python citizens. But all your suggested approaches are entirely valid, just like with any other Python class.

@hynek hynek added the Question label Jun 13, 2023
@poneill
Copy link
Author

poneill commented Jun 13, 2023

Thanks, that makes plenty of sense!

@poneill poneill closed this as completed Jun 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants