@@ -39,6 +39,7 @@ def __init__(
3939 column : int ,
4040 type : Optional [Type ],
4141 info : TypeInfo ,
42+ kw_only : bool ,
4243 ) -> None :
4344 self .name = name
4445 self .is_in_init = is_in_init
@@ -48,6 +49,7 @@ def __init__(
4849 self .column = column
4950 self .type = type
5051 self .info = info
52+ self .kw_only = kw_only
5153
5254 def to_argument (self ) -> Argument :
5355 return Argument (
@@ -77,6 +79,8 @@ def deserialize(
7779 cls , info : TypeInfo , data : JsonDict , api : SemanticAnalyzerPluginInterface
7880 ) -> 'DataclassAttribute' :
7981 data = data .copy ()
82+ if data .get ('kw_only' ) is None :
83+ data ['kw_only' ] = False
8084 typ = deserialize_and_fixup_type (data .pop ('type' ), api )
8185 return cls (type = typ , info = info , ** data )
8286
@@ -215,6 +219,7 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]:
215219 cls = self ._ctx .cls
216220 attrs : List [DataclassAttribute ] = []
217221 known_attrs : Set [str ] = set ()
222+ kw_only = _get_decorator_bool_argument (ctx , 'kw_only' , False )
218223 for stmt in cls .defs .body :
219224 # Any assignment that doesn't use the new type declaration
220225 # syntax can be ignored out of hand.
@@ -251,6 +256,10 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]:
251256 is_init_var = True
252257 node .type = node_type .args [0 ]
253258
259+ if (isinstance (node_type , Instance ) and
260+ node_type .type .fullname == 'dataclasses._KW_ONLY_TYPE' ):
261+ kw_only = True
262+
254263 has_field_call , field_args = _collect_field_args (stmt .rvalue )
255264
256265 is_in_init_param = field_args .get ('init' )
@@ -274,6 +283,13 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]:
274283 # on self in the generated __init__(), not in the class body.
275284 sym .implicit = True
276285
286+ is_kw_only = kw_only
287+ # Use the kw_only field arg if it is provided. Otherwise use the
288+ # kw_only value from the decorator parameter.
289+ field_kw_only_param = field_args .get ('kw_only' )
290+ if field_kw_only_param is not None :
291+ is_kw_only = bool (ctx .api .parse_bool (field_kw_only_param ))
292+
277293 known_attrs .add (lhs .name )
278294 attrs .append (DataclassAttribute (
279295 name = lhs .name ,
@@ -284,6 +300,7 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]:
284300 column = stmt .column ,
285301 type = sym .type ,
286302 info = cls .info ,
303+ kw_only = is_kw_only ,
287304 ))
288305
289306 # Next, collect attributes belonging to any class in the MRO
@@ -323,10 +340,10 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]:
323340 # arguments that have a default.
324341 found_default = False
325342 for attr in all_attrs :
326- # If we find any attribute that is_in_init but that
343+ # If we find any attribute that is_in_init, not kw_only, and that
327344 # doesn't have a default after one that does have one,
328345 # then that's an error.
329- if found_default and attr .is_in_init and not attr .has_default :
346+ if found_default and attr .is_in_init and not attr .has_default and not attr . kw_only :
330347 # If the issue comes from merging different classes, report it
331348 # at the class definition point.
332349 context = (Context (line = attr .line , column = attr .column ) if attr in attrs
0 commit comments