File tree Expand file tree Collapse file tree 4 files changed +47
-5
lines changed Expand file tree Collapse file tree 4 files changed +47
-5
lines changed Original file line number Diff line number Diff line change @@ -13,6 +13,10 @@ What's New in astroid 3.3.3?
1313============================
1414Release date: TBA
1515
16+ * Add annotation-only instance attributes to attrs classes to fix `no-member` false positives.
17+
18+ Closes #2514
19+
1620
1721
1822What's New in astroid 3.3.2?
Original file line number Diff line number Diff line change 2424 "field" ,
2525 )
2626)
27+ NEW_ATTRS_NAMES = frozenset (
28+ (
29+ "attrs.define" ,
30+ "attrs.mutable" ,
31+ "attrs.frozen" ,
32+ )
33+ )
2734ATTRS_NAMES = frozenset (
2835 (
2936 "attr.s" ,
3340 "attr.define" ,
3441 "attr.mutable" ,
3542 "attr.frozen" ,
36- "attrs.define" ,
37- "attrs.mutable" ,
38- "attrs.frozen" ,
43+ * NEW_ATTRS_NAMES ,
3944 )
4045)
4146
@@ -64,13 +69,14 @@ def attr_attributes_transform(node: ClassDef) -> None:
6469 # Prevents https://github.com/pylint-dev/pylint/issues/1884
6570 node .locals ["__attrs_attrs__" ] = [Unknown (parent = node )]
6671
72+ use_bare_annotations = is_decorated_with_attrs (node , NEW_ATTRS_NAMES )
6773 for cdef_body_node in node .body :
6874 if not isinstance (cdef_body_node , (Assign , AnnAssign )):
6975 continue
7076 if isinstance (cdef_body_node .value , Call ):
7177 if cdef_body_node .value .func .as_string () not in ATTRIB_NAMES :
7278 continue
73- else :
79+ elif not use_bare_annotations :
7480 continue
7581 targets = (
7682 cdef_body_node .targets
Original file line number Diff line number Diff line change 8585 "name" : " Hippo91" ,
8686 "team" : " Maintainers"
8787 },
88+ "Hnasar@users.noreply.github.com" : {
89+ "mails" : [" Hnasar@users.noreply.github.com" , " hashem@hudson-trading.com" ],
90+ "name" : " Hashem Nasarat"
91+ },
8892 "hugovk@users.noreply.github.com" : {
8993 "mails" : [" hugovk@users.noreply.github.com" ],
9094 "name" : " Hugo van Kemenade"
Original file line number Diff line number Diff line change 77import unittest
88
99import astroid
10- from astroid import nodes
10+ from astroid import exceptions , nodes
1111
1212try :
1313 import attr # type: ignore[import] # pylint: disable=unused-import
@@ -201,3 +201,31 @@ class Foo:
201201 """
202202 should_be_unknown = next (astroid .extract_node (code ).infer ()).getattr ("bar" )[0 ]
203203 self .assertIsInstance (should_be_unknown , astroid .Unknown )
204+
205+ def test_attr_with_only_annotation_fails (self ) -> None :
206+ code = """
207+ import attr
208+
209+ @attr.s
210+ class Foo:
211+ bar: int
212+ Foo()
213+ """
214+ with self .assertRaises (exceptions .AttributeInferenceError ):
215+ next (astroid .extract_node (code ).infer ()).getattr ("bar" )
216+
217+ def test_attrs_with_only_annotation_works (self ) -> None :
218+ code = """
219+ import attrs
220+
221+ @attrs.define
222+ class Foo:
223+ bar: int
224+ baz: str = "hello"
225+ Foo(1)
226+ """
227+ for attr_name in ("bar" , "baz" ):
228+ should_be_unknown = next (astroid .extract_node (code ).infer ()).getattr (
229+ attr_name
230+ )[0 ]
231+ self .assertIsInstance (should_be_unknown , astroid .Unknown )
You can’t perform that action at this time.
0 commit comments