-
-
Notifications
You must be signed in to change notification settings - Fork 409
Description
Converters can currently be specified as _ConverterType, list[_ConverterType], or tuple[_ConverterType]. The last of these does not mean "tuple of _ConverterType instances" as was likely intended; it specifies a tuple with exactly one _ConverterType element. The correct form for a tuple with any number of instances is tuple[_ConverterType, ...].
A pull request will be created shortly.
I was tempted to change the parameter's type by replacing both the list and tuple expressions with a unified Sequence[_ConverterType], but that currently doesn't work at runtime (attrs seems to treat sequences other than list and tuple as if they should be callable converters, rather than an ordered collection of converters):
from __future__ import annotations
from collections.abc import (
Sequence,
)
from typing import (
Self,
TypeVar,
overload,
)
import attrs
_T = TypeVar('_T')
class ThirdSequenceType(Sequence[_T]):
_payload: tuple[_T, ...]
def __init__(self, value: Sequence[_T]) -> None:
self._payload = tuple(value)
def __len__(self) -> int:
return len(self._payload)
@overload
def __getitem__(self, index: int) -> _T: ...
@overload
def __getitem__(self, index: slice) -> Self: ...
def __getitem__(self, index: int | slice) -> _T | Self:
if isinstance(index, slice):
return type(self)(self._payload[index])
return self._payload[index]
def negate(value: int) -> int:
return -value
def square(value: int) -> int:
return value * value
@attrs.define
class SomeClass:
value: int = attrs.field(converter=ThirdSequenceType([square, negate]))
inst = SomeClass(3)Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "..../bug.py", line 56, in <module>
inst = SomeClass(3)
^^^^^^^^^^^^
File "<attrs generated methods __main__.SomeClass>", line 24, in __init__
TypeError: 'ThirdSequenceType' object is not callable
If we wrap the argument to converter= in tuple(), i.e., change the last few lines to:
@attrs.define
class SomeClass:
value: int = attrs.field(converter=tuple(ThirdSequenceType([square, negate])))
inst = SomeClass(3)
print(inst.value) # prints: -9... then everything works as expected. My interpretation is that ThirdSequenceType is a perfectly well-behaved Sequence, but not one that attrs.field(converter=...) is willing to accept.