How to mypy typehint an attrs validator attribute

239 views Asked by At

Given the following:

from pathlib import Path

import attr


@attr.define(frozen=False)
class ExampleAtt:
    x: str = attr.field()

    @x.validator
    def check(self, attribute: attr.Attribute, value: str) -> None:
        expected_values = ['a', 'b', 'c']
        if value not in expected_values:
            raise ValueError('error')

I get the mypy error:

error: Missing type parameters for generic type "Attribute"  [type-arg]

How can I type hint an attrs attribute used within a validator ? I've tried what's in the code above, as well as attr._make.Attribute found from type(attribute).

2

There are 2 answers

0
Clasherkasten On

Not mentioned in the docs, but evident by the code and mypys output you provided, attr.Attribute inherits from Generic which makes it itself a generic type.

To get rid of the error, provide the the concrete type for this case (here str, as you want to validate attribute x which is of this type).

0
Tin Tvrtković On

I'm a maintainer of attrs.

I gather you're using the --disallow-any-generics Mypy option. A small problem here is that attrs.Attribute isn't really a generic class, but the Mypy attrs plugin treats it as such because it's more useful that way. I think we'll change this in the future so it's generic in attrs too.

But in the meantime, what you want to do is using a string annotation instead. Here:

import attrs


@attrs.define()
class ExampleAtt:
    x: str = attrs.field()

    @x.validator
    def check(self, attribute: "attrs.Attribute[str]", value: str) -> None:
        expected_values = ["a", "b", "c"]
        if value not in expected_values:
            raise ValueError("error")

I've switched to the new attrs namespace and cleaned up the example a little. Note that the type of the attribute parameter, attrs.Attribute[str] is now generic (Attribute[str] vs just a bare Attribute), and the type annotation is in quotes (i.e. a string).

Another solution would be to use from __future__ import annotations but that's a bigger change, so I would use the first.