Skip to content

Commit c028053

Browse files
authored
Add keyword-only fields to dataclasses. (GH=25608)
1 parent 7f8e072 commit c028053

File tree

4 files changed

+298
-38
lines changed

4 files changed

+298
-38
lines changed

Doc/library/dataclasses.rst

+36-6
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ directly specified in the ``InventoryItem`` definition shown above.
4646
Module-level decorators, classes, and functions
4747
-----------------------------------------------
4848

49-
.. decorator:: dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True)
49+
.. decorator:: dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False)
5050

5151
This function is a :term:`decorator` that is used to add generated
5252
:term:`special method`\s to classes, as described below.
@@ -79,7 +79,7 @@ Module-level decorators, classes, and functions
7979
class C:
8080
...
8181

82-
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True)
82+
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False)
8383
class C:
8484
...
8585

@@ -168,6 +168,10 @@ Module-level decorators, classes, and functions
168168
``__match_args__`` is already defined in the class, then
169169
``__match_args__`` will not be generated.
170170

171+
- ``kw_only``: If true (the default value is ``False``), then all
172+
fields will be marked as keyword-only. See the :term:`parameter`
173+
glossary entry for details. Also see the ``dataclasses.KW_ONLY``
174+
section.
171175

172176
``field``\s may optionally specify a default value, using normal
173177
Python syntax::
@@ -333,17 +337,17 @@ Module-level decorators, classes, and functions
333337

334338
Raises :exc:`TypeError` if ``instance`` is not a dataclass instance.
335339

336-
.. function:: make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True)
340+
.. function:: make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False)
337341

338342
Creates a new dataclass with name ``cls_name``, fields as defined
339343
in ``fields``, base classes as given in ``bases``, and initialized
340344
with a namespace as given in ``namespace``. ``fields`` is an
341345
iterable whose elements are each either ``name``, ``(name, type)``,
342346
or ``(name, type, Field)``. If just ``name`` is supplied,
343347
``typing.Any`` is used for ``type``. The values of ``init``,
344-
``repr``, ``eq``, ``order``, ``unsafe_hash``, ``frozen``, and
345-
``match_args`` have the same meaning as they do in
346-
:func:`dataclass`.
348+
``repr``, ``eq``, ``order``, ``unsafe_hash``, ``frozen``,
349+
``match_args``, and ``kw_only`` have the same meaning as they do
350+
in :func:`dataclass`.
347351

348352
This function is not strictly required, because any Python
349353
mechanism for creating a new class with ``__annotations__`` can
@@ -520,6 +524,32 @@ The generated :meth:`__init__` method for ``C`` will look like::
520524

521525
def __init__(self, x: int = 15, y: int = 0, z: int = 10):
522526

527+
Re-ordering of keyword-only parameters in __init__
528+
--------------------------------------------------
529+
530+
After the fields needed for :meth:`__init__` are computed, any
531+
keyword-only fields are put after regular fields. In this example,
532+
``Base.y`` and ``D.t`` are keyword-only fields::
533+
534+
@dataclass
535+
class Base:
536+
x: Any = 15.0
537+
_: KW_ONLY
538+
y: int = 0
539+
540+
@dataclass
541+
class D(Base):
542+
z: int = 10
543+
t: int = field(kw_only=True, default=0)
544+
545+
The generated :meth:`__init__` method for ``D`` will look like::
546+
547+
def __init__(self, x: Any = 15.0, z: int = 10, *, y: int = 0, t: int = 0):
548+
549+
The relative ordering of keyword-only arguments is not changed from
550+
the order they are in computed field :meth:`__init__` list.
551+
552+
523553
Default factory functions
524554
-------------------------
525555

Lib/dataclasses.py

+101-32
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
'Field',
1717
'FrozenInstanceError',
1818
'InitVar',
19+
'KW_ONLY',
1920
'MISSING',
2021

2122
# Helper functions.
@@ -163,8 +164,8 @@
163164
# +-------+-------+-------+
164165
# | True | add | | <- the default
165166
# +=======+=======+=======+
166-
# __match_args__ is a tuple of __init__ parameter names; non-init fields must
167-
# be matched by keyword.
167+
# __match_args__ is always added unless the class already defines it. It is a
168+
# tuple of __init__ parameter names; non-init fields must be matched by keyword.
168169

169170

170171
# Raised when an attempt is made to modify a frozen class.
@@ -184,6 +185,12 @@ class _MISSING_TYPE:
184185
pass
185186
MISSING = _MISSING_TYPE()
186187

188+
# A sentinel object to indicate that following fields are keyword-only by
189+
# default. Use a class to give it a better repr.
190+
class _KW_ONLY_TYPE:
191+
pass
192+
KW_ONLY = _KW_ONLY_TYPE()
193+
187194
# Since most per-field metadata will be unused, create an empty
188195
# read-only proxy that can be shared among all fields.
189196
_EMPTY_METADATA = types.MappingProxyType({})
@@ -232,7 +239,6 @@ def __repr__(self):
232239
def __class_getitem__(cls, type):
233240
return InitVar(type)
234241

235-
236242
# Instances of Field are only ever created from within this module,
237243
# and only from the field() function, although Field instances are
238244
# exposed externally as (conceptually) read-only objects.
@@ -253,11 +259,12 @@ class Field:
253259
'init',
254260
'compare',
255261
'metadata',
262+
'kw_only',
256263
'_field_type', # Private: not to be used by user code.
257264
)
258265

259266
def __init__(self, default, default_factory, init, repr, hash, compare,
260-
metadata):
267+
metadata, kw_only):
261268
self.name = None
262269
self.type = None
263270
self.default = default
@@ -269,6 +276,7 @@ def __init__(self, default, default_factory, init, repr, hash, compare,
269276
self.metadata = (_EMPTY_METADATA
270277
if metadata is None else
271278
types.MappingProxyType(metadata))
279+
self.kw_only = kw_only
272280
self._field_type = None
273281

274282
def __repr__(self):
@@ -282,6 +290,7 @@ def __repr__(self):
282290
f'hash={self.hash!r},'
283291
f'compare={self.compare!r},'
284292
f'metadata={self.metadata!r},'
293+
f'kw_only={self.kw_only!r},'
285294
f'_field_type={self._field_type}'
286295
')')
287296

@@ -335,25 +344,36 @@ def __repr__(self):
335344
# so that a type checker can be told (via overloads) that this is a
336345
# function whose type depends on its parameters.
337346
def field(*, default=MISSING, default_factory=MISSING, init=True, repr=True,
338-
hash=None, compare=True, metadata=None):
347+
hash=None, compare=True, metadata=None, kw_only=MISSING):
339348
"""Return an object to identify dataclass fields.
340349
341350
default is the default value of the field. default_factory is a
342351
0-argument function called to initialize a field's value. If init
343-
is True, the field will be a parameter to the class's __init__()
344-
function. If repr is True, the field will be included in the
345-
object's repr(). If hash is True, the field will be included in
346-
the object's hash(). If compare is True, the field will be used
347-
in comparison functions. metadata, if specified, must be a
348-
mapping which is stored but not otherwise examined by dataclass.
352+
is true, the field will be a parameter to the class's __init__()
353+
function. If repr is true, the field will be included in the
354+
object's repr(). If hash is true, the field will be included in the
355+
object's hash(). If compare is true, the field will be used in
356+
comparison functions. metadata, if specified, must be a mapping
357+
which is stored but not otherwise examined by dataclass. If kw_only
358+
is true, the field will become a keyword-only parameter to
359+
__init__().
349360
350361
It is an error to specify both default and default_factory.
351362
"""
352363

353364
if default is not MISSING and default_factory is not MISSING:
354365
raise ValueError('cannot specify both default and default_factory')
355366
return Field(default, default_factory, init, repr, hash, compare,
356-
metadata)
367+
metadata, kw_only)
368+
369+
370+
def _fields_in_init_order(fields):
371+
# Returns the fields as __init__ will output them. It returns 2 tuples:
372+
# the first for normal args, and the second for keyword args.
373+
374+
return (tuple(f for f in fields if f.init and not f.kw_only),
375+
tuple(f for f in fields if f.init and f.kw_only)
376+
)
357377

358378

359379
def _tuple_str(obj_name, fields):
@@ -410,7 +430,6 @@ def _create_fn(name, args, body, *, globals=None, locals=None,
410430

411431
local_vars = ', '.join(locals.keys())
412432
txt = f"def __create_fn__({local_vars}):\n{txt}\n return {name}"
413-
414433
ns = {}
415434
exec(txt, globals, ns)
416435
return ns['__create_fn__'](**locals)
@@ -501,17 +520,19 @@ def _init_param(f):
501520
return f'{f.name}:_type_{f.name}{default}'
502521

503522

504-
def _init_fn(fields, frozen, has_post_init, self_name, globals):
523+
def _init_fn(fields, std_fields, kw_only_fields, frozen, has_post_init,
524+
self_name, globals):
505525
# fields contains both real fields and InitVar pseudo-fields.
506526

507527
# Make sure we don't have fields without defaults following fields
508528
# with defaults. This actually would be caught when exec-ing the
509529
# function source code, but catching it here gives a better error
510530
# message, and future-proofs us in case we build up the function
511531
# using ast.
532+
512533
seen_default = False
513-
for f in fields:
514-
# Only consider fields in the __init__ call.
534+
for f in std_fields:
535+
# Only consider the non-kw-only fields in the __init__ call.
515536
if f.init:
516537
if not (f.default is MISSING and f.default_factory is MISSING):
517538
seen_default = True
@@ -543,8 +564,15 @@ def _init_fn(fields, frozen, has_post_init, self_name, globals):
543564
if not body_lines:
544565
body_lines = ['pass']
545566

567+
_init_params = [_init_param(f) for f in std_fields]
568+
if kw_only_fields:
569+
# Add the keyword-only args. Because the * can only be added if
570+
# there's at least one keyword-only arg, there needs to be a test here
571+
# (instead of just concatenting the lists together).
572+
_init_params += ['*']
573+
_init_params += [_init_param(f) for f in kw_only_fields]
546574
return _create_fn('__init__',
547-
[self_name] + [_init_param(f) for f in fields if f.init],
575+
[self_name] + _init_params,
548576
body_lines,
549577
locals=locals,
550578
globals=globals,
@@ -623,6 +651,9 @@ def _is_initvar(a_type, dataclasses):
623651
return (a_type is dataclasses.InitVar
624652
or type(a_type) is dataclasses.InitVar)
625653

654+
def _is_kw_only(a_type, dataclasses):
655+
return a_type is dataclasses.KW_ONLY
656+
626657

627658
def _is_type(annotation, cls, a_module, a_type, is_type_predicate):
628659
# Given a type annotation string, does it refer to a_type in
@@ -683,10 +714,11 @@ def _is_type(annotation, cls, a_module, a_type, is_type_predicate):
683714
return False
684715

685716

686-
def _get_field(cls, a_name, a_type):
687-
# Return a Field object for this field name and type. ClassVars
688-
# and InitVars are also returned, but marked as such (see
689-
# f._field_type).
717+
def _get_field(cls, a_name, a_type, default_kw_only):
718+
# Return a Field object for this field name and type. ClassVars and
719+
# InitVars are also returned, but marked as such (see f._field_type).
720+
# default_kw_only is the value of kw_only to use if there isn't a field()
721+
# that defines it.
690722

691723
# If the default value isn't derived from Field, then it's only a
692724
# normal default value. Convert it to a Field().
@@ -757,6 +789,19 @@ def _get_field(cls, a_name, a_type):
757789
# init=<not-the-default-init-value>)? It makes no sense for
758790
# ClassVar and InitVar to specify init=<anything>.
759791

792+
# kw_only validation and assignment.
793+
if f._field_type in (_FIELD, _FIELD_INITVAR):
794+
# For real and InitVar fields, if kw_only wasn't specified use the
795+
# default value.
796+
if f.kw_only is MISSING:
797+
f.kw_only = default_kw_only
798+
else:
799+
# Make sure kw_only isn't set for ClassVars
800+
assert f._field_type is _FIELD_CLASSVAR
801+
if f.kw_only is not MISSING:
802+
raise TypeError(f'field {f.name} is a ClassVar but specifies '
803+
'kw_only')
804+
760805
# For real fields, disallow mutable defaults for known types.
761806
if f._field_type is _FIELD and isinstance(f.default, (list, dict, set)):
762807
raise ValueError(f'mutable default {type(f.default)} for field '
@@ -829,7 +874,7 @@ def _hash_exception(cls, fields, globals):
829874

830875

831876
def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
832-
match_args):
877+
match_args, kw_only):
833878
# Now that dicts retain insertion order, there's no reason to use
834879
# an ordered dict. I am leveraging that ordering here, because
835880
# derived class fields overwrite base class fields, but the order
@@ -883,8 +928,22 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
883928
# Now find fields in our class. While doing so, validate some
884929
# things, and set the default values (as class attributes) where
885930
# we can.
886-
cls_fields = [_get_field(cls, name, type)
887-
for name, type in cls_annotations.items()]
931+
cls_fields = []
932+
# Get a reference to this module for the _is_kw_only() test.
933+
dataclasses = sys.modules[__name__]
934+
for name, type in cls_annotations.items():
935+
# See if this is a marker to change the value of kw_only.
936+
if (_is_kw_only(type, dataclasses)
937+
or (isinstance(type, str)
938+
and _is_type(type, cls, dataclasses, dataclasses.KW_ONLY,
939+
_is_kw_only))):
940+
# Switch the default to kw_only=True, and ignore this
941+
# annotation: it's not a real field.
942+
kw_only = True
943+
else:
944+
# Otherwise it's a field of some type.
945+
cls_fields.append(_get_field(cls, name, type, kw_only))
946+
888947
for f in cls_fields:
889948
fields[f.name] = f
890949

@@ -939,15 +998,22 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
939998
if order and not eq:
940999
raise ValueError('eq must be true if order is true')
9411000

1001+
# Include InitVars and regular fields (so, not ClassVars). This is
1002+
# initialized here, outside of the "if init:" test, because std_init_fields
1003+
# is used with match_args, below.
1004+
all_init_fields = [f for f in fields.values()
1005+
if f._field_type in (_FIELD, _FIELD_INITVAR)]
1006+
(std_init_fields,
1007+
kw_only_init_fields) = _fields_in_init_order(all_init_fields)
1008+
9421009
if init:
9431010
# Does this class have a post-init function?
9441011
has_post_init = hasattr(cls, _POST_INIT_NAME)
9451012

946-
# Include InitVars and regular fields (so, not ClassVars).
947-
flds = [f for f in fields.values()
948-
if f._field_type in (_FIELD, _FIELD_INITVAR)]
9491013
_set_new_attribute(cls, '__init__',
950-
_init_fn(flds,
1014+
_init_fn(all_init_fields,
1015+
std_init_fields,
1016+
kw_only_init_fields,
9511017
frozen,
9521018
has_post_init,
9531019
# The name to use for the "self"
@@ -1016,16 +1082,18 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
10161082
str(inspect.signature(cls)).replace(' -> None', ''))
10171083

10181084
if match_args:
1085+
# I could probably compute this once
10191086
_set_new_attribute(cls, '__match_args__',
1020-
tuple(f.name for f in field_list if f.init))
1087+
tuple(f.name for f in std_init_fields))
10211088

10221089
abc.update_abstractmethods(cls)
10231090

10241091
return cls
10251092

10261093

10271094
def dataclass(cls=None, /, *, init=True, repr=True, eq=True, order=False,
1028-
unsafe_hash=False, frozen=False, match_args=True):
1095+
unsafe_hash=False, frozen=False, match_args=True,
1096+
kw_only=False):
10291097
"""Returns the same class as was passed in, with dunder methods
10301098
added based on the fields defined in the class.
10311099
@@ -1036,12 +1104,13 @@ def dataclass(cls=None, /, *, init=True, repr=True, eq=True, order=False,
10361104
comparison dunder methods are added. If unsafe_hash is true, a
10371105
__hash__() method function is added. If frozen is true, fields may
10381106
not be assigned to after instance creation. If match_args is true,
1039-
the __match_args__ tuple is added.
1107+
the __match_args__ tuple is added. If kw_only is true, then by
1108+
default all fields are keyword-only.
10401109
"""
10411110

10421111
def wrap(cls):
10431112
return _process_class(cls, init, repr, eq, order, unsafe_hash,
1044-
frozen, match_args)
1113+
frozen, match_args, kw_only)
10451114

10461115
# See if we're being called as @dataclass or @dataclass().
10471116
if cls is None:

0 commit comments

Comments
 (0)