@@ -115,33 +115,23 @@ def _get_dataclass_attributes(
115115) -> Iterator [nodes .AnnAssign ]:
116116 """Yield the AnnAssign nodes of dataclass attributes for the node.
117117
118- If init is True, also include InitVars, but exclude attributes from calls to
119- field where init=False.
118+ If init is True, also include InitVars.
120119 """
121120 for assign_node in node .body :
122121 if not isinstance (assign_node , nodes .AnnAssign ) or not isinstance (
123122 assign_node .target , nodes .AssignName
124123 ):
125124 continue
126125
127- if _is_class_var (assign_node .annotation ): # type: ignore[arg-type] # annotation is never None
126+ # Annotation is never None
127+ if _is_class_var (assign_node .annotation ): # type: ignore[arg-type]
128128 continue
129129
130130 if _is_keyword_only_sentinel (assign_node .annotation ):
131131 continue
132132
133- if init :
134- value = assign_node .value
135- if (
136- isinstance (value , nodes .Call )
137- and _looks_like_dataclass_field_call (value , check_scope = False )
138- and any (
139- keyword .arg == "init" and not keyword .value .bool_value ()
140- for keyword in value .keywords
141- )
142- ):
143- continue
144- elif _is_init_var (assign_node .annotation ): # type: ignore[arg-type] # annotation is never None
133+ # Annotation is never None
134+ if not init and _is_init_var (assign_node .annotation ): # type: ignore[arg-type]
145135 continue
146136
147137 yield assign_node
@@ -231,6 +221,25 @@ def _find_arguments_from_base_classes(
231221 return pos_only , kw_only
232222
233223
224+ def _get_previous_field_default (node : nodes .ClassDef , name : str ) -> nodes .NodeNG | None :
225+ """Get the default value of a previously defined field."""
226+ for base in reversed (node .mro ()):
227+ if not base .is_dataclass :
228+ continue
229+ if name in base .locals :
230+ for assign in base .locals [name ]:
231+ if (
232+ isinstance (assign .parent , nodes .AnnAssign )
233+ and assign .parent .value
234+ and isinstance (assign .parent .value , nodes .Call )
235+ and _looks_like_dataclass_field_call (assign .parent .value )
236+ ):
237+ default = _get_field_default (assign .parent .value )
238+ if default :
239+ return default [1 ]
240+ return None
241+
242+
234243def _generate_dataclass_init (
235244 node : nodes .ClassDef , assigns : list [nodes .AnnAssign ], kw_only_decorated : bool
236245) -> str :
@@ -254,6 +263,18 @@ def _generate_dataclass_init(
254263 property_node = additional_assign
255264 break
256265
266+ is_field = isinstance (value , nodes .Call ) and _looks_like_dataclass_field_call (
267+ value , check_scope = False
268+ )
269+
270+ if is_field :
271+ # Skip any fields that have `init=False`
272+ if any (
273+ keyword .arg == "init" and not keyword .value .bool_value ()
274+ for keyword in value .keywords # type: ignore[union-attr] # value is never None
275+ ):
276+ continue
277+
257278 if _is_init_var (annotation ): # type: ignore[arg-type] # annotation is never None
258279 init_var = True
259280 if isinstance (annotation , nodes .Subscript ):
@@ -272,10 +293,8 @@ def _generate_dataclass_init(
272293 param_str = name
273294
274295 if value :
275- if isinstance (value , nodes .Call ) and _looks_like_dataclass_field_call (
276- value , check_scope = False
277- ):
278- result = _get_field_default (value )
296+ if is_field :
297+ result = _get_field_default (value ) # type: ignore[arg-type]
279298 if result :
280299 default_type , default_node = result
281300 if default_type == "default" :
@@ -296,6 +315,13 @@ def _generate_dataclass_init(
296315 param_str += f" = { next (property_node .infer_call_result ()).as_string ()} "
297316 except (InferenceError , StopIteration ):
298317 pass
318+ else :
319+ # Even with `init=False` the default value still can be propogated to
320+ # later assignments. Creating weird signatures like:
321+ # (self, a: str = 1) -> None
322+ previous_default = _get_previous_field_default (node , name )
323+ if previous_default :
324+ param_str += f" = { previous_default .as_string ()} "
299325
300326 params .append (param_str )
301327 if not init_var :
0 commit comments