Clarify next-gen auto_attribs inference rules (#742)
* Clarify next-gen auto_attribs inference rules The next-gen auto_attribs api documentation does not clearly describe cases where (a) only a subset of attributes are annotated and (b) `field` definitions are provided for a subset of fields. Update docstring to reflect current, desirable, behavior. Add tests to clarify `.define` behavior focused on fully-annotated classes with partially-defined fields, which is commonly used to add non-default behavior to a subset of a classes fields. For example: ```python @attr.define class NewSchool: x: int y: list = attr.field() @y.validator def _validate_y(self, attribute, value): if value < 0: raise ValueError("y must be positive") ``` The previous docstring *could* be read to imply that: * The new-school API will not infer auto_attribs if there are any unannotated attributes. * The new-school API will not infer auto_attribs if *any* attr.ib are defined, even if those attr.ibs are type annotated. * Update test to match PR example * Fix lint error Co-authored-by: Hynek Schlawack <hs@ox.cx>
This commit is contained in:
parent
345fb0bcac
commit
2fdf92997c
|
@ -42,8 +42,8 @@ def define(
|
|||
:param Optional[bool] auto_attribs: If set to `True` or `False`, it behaves
|
||||
exactly like `attr.s`. If left `None`, `attr.s` will try to guess:
|
||||
|
||||
1. If all attributes are annotated and no `attr.ib` is found, it assumes
|
||||
*auto_attribs=True*.
|
||||
1. If any attributes are annotated and no unannotated `attr.ib`\ s
|
||||
are found, it assumes *auto_attribs=True*.
|
||||
2. Otherwise it assumes *auto_attribs=False* and tries to collect
|
||||
`attr.ib`\ s.
|
||||
|
||||
|
|
|
@ -112,6 +112,45 @@ class TestNextGen:
|
|||
|
||||
assert OldSchool2(1) == OldSchool2(1)
|
||||
|
||||
def test_auto_attribs_detect_fields_and_annotations(self):
|
||||
"""
|
||||
define infers auto_attribs=True if fields have type annotations
|
||||
"""
|
||||
|
||||
@attr.define
|
||||
class NewSchool:
|
||||
x: int
|
||||
y: list = attr.field()
|
||||
|
||||
@y.validator
|
||||
def _validate_y(self, attribute, value):
|
||||
if value < 0:
|
||||
raise ValueError("y must be positive")
|
||||
|
||||
assert NewSchool(1, 1) == NewSchool(1, 1)
|
||||
with pytest.raises(ValueError):
|
||||
NewSchool(1, -1)
|
||||
assert list(attr.fields_dict(NewSchool).keys()) == ["x", "y"]
|
||||
|
||||
def test_auto_attribs_partially_annotated(self):
|
||||
"""
|
||||
define infers auto_attribs=True if any type annotations are found
|
||||
"""
|
||||
|
||||
@attr.define
|
||||
class NewSchool:
|
||||
x: int
|
||||
y: list
|
||||
z = 10
|
||||
|
||||
# fields are defined for any annotated attributes
|
||||
assert NewSchool(1, []) == NewSchool(1, [])
|
||||
assert list(attr.fields_dict(NewSchool).keys()) == ["x", "y"]
|
||||
|
||||
# while the unannotated attributes are left as class vars
|
||||
assert NewSchool.z == 10
|
||||
assert "z" in NewSchool.__dict__
|
||||
|
||||
def test_auto_attribs_detect_annotations(self):
|
||||
"""
|
||||
define correctly detects if a class has type annotations.
|
||||
|
|
Loading…
Reference in New Issue