1*cda5da8dSAndroid Build Coastguard Workerimport re 2*cda5da8dSAndroid Build Coastguard Workerimport sys 3*cda5da8dSAndroid Build Coastguard Workerimport copy 4*cda5da8dSAndroid Build Coastguard Workerimport types 5*cda5da8dSAndroid Build Coastguard Workerimport inspect 6*cda5da8dSAndroid Build Coastguard Workerimport keyword 7*cda5da8dSAndroid Build Coastguard Workerimport builtins 8*cda5da8dSAndroid Build Coastguard Workerimport functools 9*cda5da8dSAndroid Build Coastguard Workerimport itertools 10*cda5da8dSAndroid Build Coastguard Workerimport abc 11*cda5da8dSAndroid Build Coastguard Workerimport _thread 12*cda5da8dSAndroid Build Coastguard Workerfrom types import FunctionType, GenericAlias 13*cda5da8dSAndroid Build Coastguard Worker 14*cda5da8dSAndroid Build Coastguard Worker 15*cda5da8dSAndroid Build Coastguard Worker__all__ = ['dataclass', 16*cda5da8dSAndroid Build Coastguard Worker 'field', 17*cda5da8dSAndroid Build Coastguard Worker 'Field', 18*cda5da8dSAndroid Build Coastguard Worker 'FrozenInstanceError', 19*cda5da8dSAndroid Build Coastguard Worker 'InitVar', 20*cda5da8dSAndroid Build Coastguard Worker 'KW_ONLY', 21*cda5da8dSAndroid Build Coastguard Worker 'MISSING', 22*cda5da8dSAndroid Build Coastguard Worker 23*cda5da8dSAndroid Build Coastguard Worker # Helper functions. 24*cda5da8dSAndroid Build Coastguard Worker 'fields', 25*cda5da8dSAndroid Build Coastguard Worker 'asdict', 26*cda5da8dSAndroid Build Coastguard Worker 'astuple', 27*cda5da8dSAndroid Build Coastguard Worker 'make_dataclass', 28*cda5da8dSAndroid Build Coastguard Worker 'replace', 29*cda5da8dSAndroid Build Coastguard Worker 'is_dataclass', 30*cda5da8dSAndroid Build Coastguard Worker ] 31*cda5da8dSAndroid Build Coastguard Worker 32*cda5da8dSAndroid Build Coastguard Worker# Conditions for adding methods. The boxes indicate what action the 33*cda5da8dSAndroid Build Coastguard Worker# dataclass decorator takes. For all of these tables, when I talk 34*cda5da8dSAndroid Build Coastguard Worker# about init=, repr=, eq=, order=, unsafe_hash=, or frozen=, I'm 35*cda5da8dSAndroid Build Coastguard Worker# referring to the arguments to the @dataclass decorator. When 36*cda5da8dSAndroid Build Coastguard Worker# checking if a dunder method already exists, I mean check for an 37*cda5da8dSAndroid Build Coastguard Worker# entry in the class's __dict__. I never check to see if an attribute 38*cda5da8dSAndroid Build Coastguard Worker# is defined in a base class. 39*cda5da8dSAndroid Build Coastguard Worker 40*cda5da8dSAndroid Build Coastguard Worker# Key: 41*cda5da8dSAndroid Build Coastguard Worker# +=========+=========================================+ 42*cda5da8dSAndroid Build Coastguard Worker# + Value | Meaning | 43*cda5da8dSAndroid Build Coastguard Worker# +=========+=========================================+ 44*cda5da8dSAndroid Build Coastguard Worker# | <blank> | No action: no method is added. | 45*cda5da8dSAndroid Build Coastguard Worker# +---------+-----------------------------------------+ 46*cda5da8dSAndroid Build Coastguard Worker# | add | Generated method is added. | 47*cda5da8dSAndroid Build Coastguard Worker# +---------+-----------------------------------------+ 48*cda5da8dSAndroid Build Coastguard Worker# | raise | TypeError is raised. | 49*cda5da8dSAndroid Build Coastguard Worker# +---------+-----------------------------------------+ 50*cda5da8dSAndroid Build Coastguard Worker# | None | Attribute is set to None. | 51*cda5da8dSAndroid Build Coastguard Worker# +=========+=========================================+ 52*cda5da8dSAndroid Build Coastguard Worker 53*cda5da8dSAndroid Build Coastguard Worker# __init__ 54*cda5da8dSAndroid Build Coastguard Worker# 55*cda5da8dSAndroid Build Coastguard Worker# +--- init= parameter 56*cda5da8dSAndroid Build Coastguard Worker# | 57*cda5da8dSAndroid Build Coastguard Worker# v | | | 58*cda5da8dSAndroid Build Coastguard Worker# | no | yes | <--- class has __init__ in __dict__? 59*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+ 60*cda5da8dSAndroid Build Coastguard Worker# | False | | | 61*cda5da8dSAndroid Build Coastguard Worker# +-------+-------+-------+ 62*cda5da8dSAndroid Build Coastguard Worker# | True | add | | <- the default 63*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+ 64*cda5da8dSAndroid Build Coastguard Worker 65*cda5da8dSAndroid Build Coastguard Worker# __repr__ 66*cda5da8dSAndroid Build Coastguard Worker# 67*cda5da8dSAndroid Build Coastguard Worker# +--- repr= parameter 68*cda5da8dSAndroid Build Coastguard Worker# | 69*cda5da8dSAndroid Build Coastguard Worker# v | | | 70*cda5da8dSAndroid Build Coastguard Worker# | no | yes | <--- class has __repr__ in __dict__? 71*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+ 72*cda5da8dSAndroid Build Coastguard Worker# | False | | | 73*cda5da8dSAndroid Build Coastguard Worker# +-------+-------+-------+ 74*cda5da8dSAndroid Build Coastguard Worker# | True | add | | <- the default 75*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+ 76*cda5da8dSAndroid Build Coastguard Worker 77*cda5da8dSAndroid Build Coastguard Worker 78*cda5da8dSAndroid Build Coastguard Worker# __setattr__ 79*cda5da8dSAndroid Build Coastguard Worker# __delattr__ 80*cda5da8dSAndroid Build Coastguard Worker# 81*cda5da8dSAndroid Build Coastguard Worker# +--- frozen= parameter 82*cda5da8dSAndroid Build Coastguard Worker# | 83*cda5da8dSAndroid Build Coastguard Worker# v | | | 84*cda5da8dSAndroid Build Coastguard Worker# | no | yes | <--- class has __setattr__ or __delattr__ in __dict__? 85*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+ 86*cda5da8dSAndroid Build Coastguard Worker# | False | | | <- the default 87*cda5da8dSAndroid Build Coastguard Worker# +-------+-------+-------+ 88*cda5da8dSAndroid Build Coastguard Worker# | True | add | raise | 89*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+ 90*cda5da8dSAndroid Build Coastguard Worker# Raise because not adding these methods would break the "frozen-ness" 91*cda5da8dSAndroid Build Coastguard Worker# of the class. 92*cda5da8dSAndroid Build Coastguard Worker 93*cda5da8dSAndroid Build Coastguard Worker# __eq__ 94*cda5da8dSAndroid Build Coastguard Worker# 95*cda5da8dSAndroid Build Coastguard Worker# +--- eq= parameter 96*cda5da8dSAndroid Build Coastguard Worker# | 97*cda5da8dSAndroid Build Coastguard Worker# v | | | 98*cda5da8dSAndroid Build Coastguard Worker# | no | yes | <--- class has __eq__ in __dict__? 99*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+ 100*cda5da8dSAndroid Build Coastguard Worker# | False | | | 101*cda5da8dSAndroid Build Coastguard Worker# +-------+-------+-------+ 102*cda5da8dSAndroid Build Coastguard Worker# | True | add | | <- the default 103*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+ 104*cda5da8dSAndroid Build Coastguard Worker 105*cda5da8dSAndroid Build Coastguard Worker# __lt__ 106*cda5da8dSAndroid Build Coastguard Worker# __le__ 107*cda5da8dSAndroid Build Coastguard Worker# __gt__ 108*cda5da8dSAndroid Build Coastguard Worker# __ge__ 109*cda5da8dSAndroid Build Coastguard Worker# 110*cda5da8dSAndroid Build Coastguard Worker# +--- order= parameter 111*cda5da8dSAndroid Build Coastguard Worker# | 112*cda5da8dSAndroid Build Coastguard Worker# v | | | 113*cda5da8dSAndroid Build Coastguard Worker# | no | yes | <--- class has any comparison method in __dict__? 114*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+ 115*cda5da8dSAndroid Build Coastguard Worker# | False | | | <- the default 116*cda5da8dSAndroid Build Coastguard Worker# +-------+-------+-------+ 117*cda5da8dSAndroid Build Coastguard Worker# | True | add | raise | 118*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+ 119*cda5da8dSAndroid Build Coastguard Worker# Raise because to allow this case would interfere with using 120*cda5da8dSAndroid Build Coastguard Worker# functools.total_ordering. 121*cda5da8dSAndroid Build Coastguard Worker 122*cda5da8dSAndroid Build Coastguard Worker# __hash__ 123*cda5da8dSAndroid Build Coastguard Worker 124*cda5da8dSAndroid Build Coastguard Worker# +------------------- unsafe_hash= parameter 125*cda5da8dSAndroid Build Coastguard Worker# | +----------- eq= parameter 126*cda5da8dSAndroid Build Coastguard Worker# | | +--- frozen= parameter 127*cda5da8dSAndroid Build Coastguard Worker# | | | 128*cda5da8dSAndroid Build Coastguard Worker# v v v | | | 129*cda5da8dSAndroid Build Coastguard Worker# | no | yes | <--- class has explicitly defined __hash__ 130*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+========+========+ 131*cda5da8dSAndroid Build Coastguard Worker# | False | False | False | | | No __eq__, use the base class __hash__ 132*cda5da8dSAndroid Build Coastguard Worker# +-------+-------+-------+--------+--------+ 133*cda5da8dSAndroid Build Coastguard Worker# | False | False | True | | | No __eq__, use the base class __hash__ 134*cda5da8dSAndroid Build Coastguard Worker# +-------+-------+-------+--------+--------+ 135*cda5da8dSAndroid Build Coastguard Worker# | False | True | False | None | | <-- the default, not hashable 136*cda5da8dSAndroid Build Coastguard Worker# +-------+-------+-------+--------+--------+ 137*cda5da8dSAndroid Build Coastguard Worker# | False | True | True | add | | Frozen, so hashable, allows override 138*cda5da8dSAndroid Build Coastguard Worker# +-------+-------+-------+--------+--------+ 139*cda5da8dSAndroid Build Coastguard Worker# | True | False | False | add | raise | Has no __eq__, but hashable 140*cda5da8dSAndroid Build Coastguard Worker# +-------+-------+-------+--------+--------+ 141*cda5da8dSAndroid Build Coastguard Worker# | True | False | True | add | raise | Has no __eq__, but hashable 142*cda5da8dSAndroid Build Coastguard Worker# +-------+-------+-------+--------+--------+ 143*cda5da8dSAndroid Build Coastguard Worker# | True | True | False | add | raise | Not frozen, but hashable 144*cda5da8dSAndroid Build Coastguard Worker# +-------+-------+-------+--------+--------+ 145*cda5da8dSAndroid Build Coastguard Worker# | True | True | True | add | raise | Frozen, so hashable 146*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+========+========+ 147*cda5da8dSAndroid Build Coastguard Worker# For boxes that are blank, __hash__ is untouched and therefore 148*cda5da8dSAndroid Build Coastguard Worker# inherited from the base class. If the base is object, then 149*cda5da8dSAndroid Build Coastguard Worker# id-based hashing is used. 150*cda5da8dSAndroid Build Coastguard Worker# 151*cda5da8dSAndroid Build Coastguard Worker# Note that a class may already have __hash__=None if it specified an 152*cda5da8dSAndroid Build Coastguard Worker# __eq__ method in the class body (not one that was created by 153*cda5da8dSAndroid Build Coastguard Worker# @dataclass). 154*cda5da8dSAndroid Build Coastguard Worker# 155*cda5da8dSAndroid Build Coastguard Worker# See _hash_action (below) for a coded version of this table. 156*cda5da8dSAndroid Build Coastguard Worker 157*cda5da8dSAndroid Build Coastguard Worker# __match_args__ 158*cda5da8dSAndroid Build Coastguard Worker# 159*cda5da8dSAndroid Build Coastguard Worker# +--- match_args= parameter 160*cda5da8dSAndroid Build Coastguard Worker# | 161*cda5da8dSAndroid Build Coastguard Worker# v | | | 162*cda5da8dSAndroid Build Coastguard Worker# | no | yes | <--- class has __match_args__ in __dict__? 163*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+ 164*cda5da8dSAndroid Build Coastguard Worker# | False | | | 165*cda5da8dSAndroid Build Coastguard Worker# +-------+-------+-------+ 166*cda5da8dSAndroid Build Coastguard Worker# | True | add | | <- the default 167*cda5da8dSAndroid Build Coastguard Worker# +=======+=======+=======+ 168*cda5da8dSAndroid Build Coastguard Worker# __match_args__ is always added unless the class already defines it. It is a 169*cda5da8dSAndroid Build Coastguard Worker# tuple of __init__ parameter names; non-init fields must be matched by keyword. 170*cda5da8dSAndroid Build Coastguard Worker 171*cda5da8dSAndroid Build Coastguard Worker 172*cda5da8dSAndroid Build Coastguard Worker# Raised when an attempt is made to modify a frozen class. 173*cda5da8dSAndroid Build Coastguard Workerclass FrozenInstanceError(AttributeError): pass 174*cda5da8dSAndroid Build Coastguard Worker 175*cda5da8dSAndroid Build Coastguard Worker# A sentinel object for default values to signal that a default 176*cda5da8dSAndroid Build Coastguard Worker# factory will be used. This is given a nice repr() which will appear 177*cda5da8dSAndroid Build Coastguard Worker# in the function signature of dataclasses' constructors. 178*cda5da8dSAndroid Build Coastguard Workerclass _HAS_DEFAULT_FACTORY_CLASS: 179*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 180*cda5da8dSAndroid Build Coastguard Worker return '<factory>' 181*cda5da8dSAndroid Build Coastguard Worker_HAS_DEFAULT_FACTORY = _HAS_DEFAULT_FACTORY_CLASS() 182*cda5da8dSAndroid Build Coastguard Worker 183*cda5da8dSAndroid Build Coastguard Worker# A sentinel object to detect if a parameter is supplied or not. Use 184*cda5da8dSAndroid Build Coastguard Worker# a class to give it a better repr. 185*cda5da8dSAndroid Build Coastguard Workerclass _MISSING_TYPE: 186*cda5da8dSAndroid Build Coastguard Worker pass 187*cda5da8dSAndroid Build Coastguard WorkerMISSING = _MISSING_TYPE() 188*cda5da8dSAndroid Build Coastguard Worker 189*cda5da8dSAndroid Build Coastguard Worker# A sentinel object to indicate that following fields are keyword-only by 190*cda5da8dSAndroid Build Coastguard Worker# default. Use a class to give it a better repr. 191*cda5da8dSAndroid Build Coastguard Workerclass _KW_ONLY_TYPE: 192*cda5da8dSAndroid Build Coastguard Worker pass 193*cda5da8dSAndroid Build Coastguard WorkerKW_ONLY = _KW_ONLY_TYPE() 194*cda5da8dSAndroid Build Coastguard Worker 195*cda5da8dSAndroid Build Coastguard Worker# Since most per-field metadata will be unused, create an empty 196*cda5da8dSAndroid Build Coastguard Worker# read-only proxy that can be shared among all fields. 197*cda5da8dSAndroid Build Coastguard Worker_EMPTY_METADATA = types.MappingProxyType({}) 198*cda5da8dSAndroid Build Coastguard Worker 199*cda5da8dSAndroid Build Coastguard Worker# Markers for the various kinds of fields and pseudo-fields. 200*cda5da8dSAndroid Build Coastguard Workerclass _FIELD_BASE: 201*cda5da8dSAndroid Build Coastguard Worker def __init__(self, name): 202*cda5da8dSAndroid Build Coastguard Worker self.name = name 203*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 204*cda5da8dSAndroid Build Coastguard Worker return self.name 205*cda5da8dSAndroid Build Coastguard Worker_FIELD = _FIELD_BASE('_FIELD') 206*cda5da8dSAndroid Build Coastguard Worker_FIELD_CLASSVAR = _FIELD_BASE('_FIELD_CLASSVAR') 207*cda5da8dSAndroid Build Coastguard Worker_FIELD_INITVAR = _FIELD_BASE('_FIELD_INITVAR') 208*cda5da8dSAndroid Build Coastguard Worker 209*cda5da8dSAndroid Build Coastguard Worker# The name of an attribute on the class where we store the Field 210*cda5da8dSAndroid Build Coastguard Worker# objects. Also used to check if a class is a Data Class. 211*cda5da8dSAndroid Build Coastguard Worker_FIELDS = '__dataclass_fields__' 212*cda5da8dSAndroid Build Coastguard Worker 213*cda5da8dSAndroid Build Coastguard Worker# The name of an attribute on the class that stores the parameters to 214*cda5da8dSAndroid Build Coastguard Worker# @dataclass. 215*cda5da8dSAndroid Build Coastguard Worker_PARAMS = '__dataclass_params__' 216*cda5da8dSAndroid Build Coastguard Worker 217*cda5da8dSAndroid Build Coastguard Worker# The name of the function, that if it exists, is called at the end of 218*cda5da8dSAndroid Build Coastguard Worker# __init__. 219*cda5da8dSAndroid Build Coastguard Worker_POST_INIT_NAME = '__post_init__' 220*cda5da8dSAndroid Build Coastguard Worker 221*cda5da8dSAndroid Build Coastguard Worker# String regex that string annotations for ClassVar or InitVar must match. 222*cda5da8dSAndroid Build Coastguard Worker# Allows "identifier.identifier[" or "identifier[". 223*cda5da8dSAndroid Build Coastguard Worker# https://bugs.python.org/issue33453 for details. 224*cda5da8dSAndroid Build Coastguard Worker_MODULE_IDENTIFIER_RE = re.compile(r'^(?:\s*(\w+)\s*\.)?\s*(\w+)') 225*cda5da8dSAndroid Build Coastguard Worker 226*cda5da8dSAndroid Build Coastguard Worker# This function's logic is copied from "recursive_repr" function in 227*cda5da8dSAndroid Build Coastguard Worker# reprlib module to avoid dependency. 228*cda5da8dSAndroid Build Coastguard Workerdef _recursive_repr(user_function): 229*cda5da8dSAndroid Build Coastguard Worker # Decorator to make a repr function return "..." for a recursive 230*cda5da8dSAndroid Build Coastguard Worker # call. 231*cda5da8dSAndroid Build Coastguard Worker repr_running = set() 232*cda5da8dSAndroid Build Coastguard Worker 233*cda5da8dSAndroid Build Coastguard Worker @functools.wraps(user_function) 234*cda5da8dSAndroid Build Coastguard Worker def wrapper(self): 235*cda5da8dSAndroid Build Coastguard Worker key = id(self), _thread.get_ident() 236*cda5da8dSAndroid Build Coastguard Worker if key in repr_running: 237*cda5da8dSAndroid Build Coastguard Worker return '...' 238*cda5da8dSAndroid Build Coastguard Worker repr_running.add(key) 239*cda5da8dSAndroid Build Coastguard Worker try: 240*cda5da8dSAndroid Build Coastguard Worker result = user_function(self) 241*cda5da8dSAndroid Build Coastguard Worker finally: 242*cda5da8dSAndroid Build Coastguard Worker repr_running.discard(key) 243*cda5da8dSAndroid Build Coastguard Worker return result 244*cda5da8dSAndroid Build Coastguard Worker return wrapper 245*cda5da8dSAndroid Build Coastguard Worker 246*cda5da8dSAndroid Build Coastguard Workerclass InitVar: 247*cda5da8dSAndroid Build Coastguard Worker __slots__ = ('type', ) 248*cda5da8dSAndroid Build Coastguard Worker 249*cda5da8dSAndroid Build Coastguard Worker def __init__(self, type): 250*cda5da8dSAndroid Build Coastguard Worker self.type = type 251*cda5da8dSAndroid Build Coastguard Worker 252*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 253*cda5da8dSAndroid Build Coastguard Worker if isinstance(self.type, type): 254*cda5da8dSAndroid Build Coastguard Worker type_name = self.type.__name__ 255*cda5da8dSAndroid Build Coastguard Worker else: 256*cda5da8dSAndroid Build Coastguard Worker # typing objects, e.g. List[int] 257*cda5da8dSAndroid Build Coastguard Worker type_name = repr(self.type) 258*cda5da8dSAndroid Build Coastguard Worker return f'dataclasses.InitVar[{type_name}]' 259*cda5da8dSAndroid Build Coastguard Worker 260*cda5da8dSAndroid Build Coastguard Worker def __class_getitem__(cls, type): 261*cda5da8dSAndroid Build Coastguard Worker return InitVar(type) 262*cda5da8dSAndroid Build Coastguard Worker 263*cda5da8dSAndroid Build Coastguard Worker# Instances of Field are only ever created from within this module, 264*cda5da8dSAndroid Build Coastguard Worker# and only from the field() function, although Field instances are 265*cda5da8dSAndroid Build Coastguard Worker# exposed externally as (conceptually) read-only objects. 266*cda5da8dSAndroid Build Coastguard Worker# 267*cda5da8dSAndroid Build Coastguard Worker# name and type are filled in after the fact, not in __init__. 268*cda5da8dSAndroid Build Coastguard Worker# They're not known at the time this class is instantiated, but it's 269*cda5da8dSAndroid Build Coastguard Worker# convenient if they're available later. 270*cda5da8dSAndroid Build Coastguard Worker# 271*cda5da8dSAndroid Build Coastguard Worker# When cls._FIELDS is filled in with a list of Field objects, the name 272*cda5da8dSAndroid Build Coastguard Worker# and type fields will have been populated. 273*cda5da8dSAndroid Build Coastguard Workerclass Field: 274*cda5da8dSAndroid Build Coastguard Worker __slots__ = ('name', 275*cda5da8dSAndroid Build Coastguard Worker 'type', 276*cda5da8dSAndroid Build Coastguard Worker 'default', 277*cda5da8dSAndroid Build Coastguard Worker 'default_factory', 278*cda5da8dSAndroid Build Coastguard Worker 'repr', 279*cda5da8dSAndroid Build Coastguard Worker 'hash', 280*cda5da8dSAndroid Build Coastguard Worker 'init', 281*cda5da8dSAndroid Build Coastguard Worker 'compare', 282*cda5da8dSAndroid Build Coastguard Worker 'metadata', 283*cda5da8dSAndroid Build Coastguard Worker 'kw_only', 284*cda5da8dSAndroid Build Coastguard Worker '_field_type', # Private: not to be used by user code. 285*cda5da8dSAndroid Build Coastguard Worker ) 286*cda5da8dSAndroid Build Coastguard Worker 287*cda5da8dSAndroid Build Coastguard Worker def __init__(self, default, default_factory, init, repr, hash, compare, 288*cda5da8dSAndroid Build Coastguard Worker metadata, kw_only): 289*cda5da8dSAndroid Build Coastguard Worker self.name = None 290*cda5da8dSAndroid Build Coastguard Worker self.type = None 291*cda5da8dSAndroid Build Coastguard Worker self.default = default 292*cda5da8dSAndroid Build Coastguard Worker self.default_factory = default_factory 293*cda5da8dSAndroid Build Coastguard Worker self.init = init 294*cda5da8dSAndroid Build Coastguard Worker self.repr = repr 295*cda5da8dSAndroid Build Coastguard Worker self.hash = hash 296*cda5da8dSAndroid Build Coastguard Worker self.compare = compare 297*cda5da8dSAndroid Build Coastguard Worker self.metadata = (_EMPTY_METADATA 298*cda5da8dSAndroid Build Coastguard Worker if metadata is None else 299*cda5da8dSAndroid Build Coastguard Worker types.MappingProxyType(metadata)) 300*cda5da8dSAndroid Build Coastguard Worker self.kw_only = kw_only 301*cda5da8dSAndroid Build Coastguard Worker self._field_type = None 302*cda5da8dSAndroid Build Coastguard Worker 303*cda5da8dSAndroid Build Coastguard Worker @_recursive_repr 304*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 305*cda5da8dSAndroid Build Coastguard Worker return ('Field(' 306*cda5da8dSAndroid Build Coastguard Worker f'name={self.name!r},' 307*cda5da8dSAndroid Build Coastguard Worker f'type={self.type!r},' 308*cda5da8dSAndroid Build Coastguard Worker f'default={self.default!r},' 309*cda5da8dSAndroid Build Coastguard Worker f'default_factory={self.default_factory!r},' 310*cda5da8dSAndroid Build Coastguard Worker f'init={self.init!r},' 311*cda5da8dSAndroid Build Coastguard Worker f'repr={self.repr!r},' 312*cda5da8dSAndroid Build Coastguard Worker f'hash={self.hash!r},' 313*cda5da8dSAndroid Build Coastguard Worker f'compare={self.compare!r},' 314*cda5da8dSAndroid Build Coastguard Worker f'metadata={self.metadata!r},' 315*cda5da8dSAndroid Build Coastguard Worker f'kw_only={self.kw_only!r},' 316*cda5da8dSAndroid Build Coastguard Worker f'_field_type={self._field_type}' 317*cda5da8dSAndroid Build Coastguard Worker ')') 318*cda5da8dSAndroid Build Coastguard Worker 319*cda5da8dSAndroid Build Coastguard Worker # This is used to support the PEP 487 __set_name__ protocol in the 320*cda5da8dSAndroid Build Coastguard Worker # case where we're using a field that contains a descriptor as a 321*cda5da8dSAndroid Build Coastguard Worker # default value. For details on __set_name__, see 322*cda5da8dSAndroid Build Coastguard Worker # https://peps.python.org/pep-0487/#implementation-details. 323*cda5da8dSAndroid Build Coastguard Worker # 324*cda5da8dSAndroid Build Coastguard Worker # Note that in _process_class, this Field object is overwritten 325*cda5da8dSAndroid Build Coastguard Worker # with the default value, so the end result is a descriptor that 326*cda5da8dSAndroid Build Coastguard Worker # had __set_name__ called on it at the right time. 327*cda5da8dSAndroid Build Coastguard Worker def __set_name__(self, owner, name): 328*cda5da8dSAndroid Build Coastguard Worker func = getattr(type(self.default), '__set_name__', None) 329*cda5da8dSAndroid Build Coastguard Worker if func: 330*cda5da8dSAndroid Build Coastguard Worker # There is a __set_name__ method on the descriptor, call 331*cda5da8dSAndroid Build Coastguard Worker # it. 332*cda5da8dSAndroid Build Coastguard Worker func(self.default, owner, name) 333*cda5da8dSAndroid Build Coastguard Worker 334*cda5da8dSAndroid Build Coastguard Worker __class_getitem__ = classmethod(GenericAlias) 335*cda5da8dSAndroid Build Coastguard Worker 336*cda5da8dSAndroid Build Coastguard Worker 337*cda5da8dSAndroid Build Coastguard Workerclass _DataclassParams: 338*cda5da8dSAndroid Build Coastguard Worker __slots__ = ('init', 339*cda5da8dSAndroid Build Coastguard Worker 'repr', 340*cda5da8dSAndroid Build Coastguard Worker 'eq', 341*cda5da8dSAndroid Build Coastguard Worker 'order', 342*cda5da8dSAndroid Build Coastguard Worker 'unsafe_hash', 343*cda5da8dSAndroid Build Coastguard Worker 'frozen', 344*cda5da8dSAndroid Build Coastguard Worker ) 345*cda5da8dSAndroid Build Coastguard Worker 346*cda5da8dSAndroid Build Coastguard Worker def __init__(self, init, repr, eq, order, unsafe_hash, frozen): 347*cda5da8dSAndroid Build Coastguard Worker self.init = init 348*cda5da8dSAndroid Build Coastguard Worker self.repr = repr 349*cda5da8dSAndroid Build Coastguard Worker self.eq = eq 350*cda5da8dSAndroid Build Coastguard Worker self.order = order 351*cda5da8dSAndroid Build Coastguard Worker self.unsafe_hash = unsafe_hash 352*cda5da8dSAndroid Build Coastguard Worker self.frozen = frozen 353*cda5da8dSAndroid Build Coastguard Worker 354*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 355*cda5da8dSAndroid Build Coastguard Worker return ('_DataclassParams(' 356*cda5da8dSAndroid Build Coastguard Worker f'init={self.init!r},' 357*cda5da8dSAndroid Build Coastguard Worker f'repr={self.repr!r},' 358*cda5da8dSAndroid Build Coastguard Worker f'eq={self.eq!r},' 359*cda5da8dSAndroid Build Coastguard Worker f'order={self.order!r},' 360*cda5da8dSAndroid Build Coastguard Worker f'unsafe_hash={self.unsafe_hash!r},' 361*cda5da8dSAndroid Build Coastguard Worker f'frozen={self.frozen!r}' 362*cda5da8dSAndroid Build Coastguard Worker ')') 363*cda5da8dSAndroid Build Coastguard Worker 364*cda5da8dSAndroid Build Coastguard Worker 365*cda5da8dSAndroid Build Coastguard Worker# This function is used instead of exposing Field creation directly, 366*cda5da8dSAndroid Build Coastguard Worker# so that a type checker can be told (via overloads) that this is a 367*cda5da8dSAndroid Build Coastguard Worker# function whose type depends on its parameters. 368*cda5da8dSAndroid Build Coastguard Workerdef field(*, default=MISSING, default_factory=MISSING, init=True, repr=True, 369*cda5da8dSAndroid Build Coastguard Worker hash=None, compare=True, metadata=None, kw_only=MISSING): 370*cda5da8dSAndroid Build Coastguard Worker """Return an object to identify dataclass fields. 371*cda5da8dSAndroid Build Coastguard Worker 372*cda5da8dSAndroid Build Coastguard Worker default is the default value of the field. default_factory is a 373*cda5da8dSAndroid Build Coastguard Worker 0-argument function called to initialize a field's value. If init 374*cda5da8dSAndroid Build Coastguard Worker is true, the field will be a parameter to the class's __init__() 375*cda5da8dSAndroid Build Coastguard Worker function. If repr is true, the field will be included in the 376*cda5da8dSAndroid Build Coastguard Worker object's repr(). If hash is true, the field will be included in the 377*cda5da8dSAndroid Build Coastguard Worker object's hash(). If compare is true, the field will be used in 378*cda5da8dSAndroid Build Coastguard Worker comparison functions. metadata, if specified, must be a mapping 379*cda5da8dSAndroid Build Coastguard Worker which is stored but not otherwise examined by dataclass. If kw_only 380*cda5da8dSAndroid Build Coastguard Worker is true, the field will become a keyword-only parameter to 381*cda5da8dSAndroid Build Coastguard Worker __init__(). 382*cda5da8dSAndroid Build Coastguard Worker 383*cda5da8dSAndroid Build Coastguard Worker It is an error to specify both default and default_factory. 384*cda5da8dSAndroid Build Coastguard Worker """ 385*cda5da8dSAndroid Build Coastguard Worker 386*cda5da8dSAndroid Build Coastguard Worker if default is not MISSING and default_factory is not MISSING: 387*cda5da8dSAndroid Build Coastguard Worker raise ValueError('cannot specify both default and default_factory') 388*cda5da8dSAndroid Build Coastguard Worker return Field(default, default_factory, init, repr, hash, compare, 389*cda5da8dSAndroid Build Coastguard Worker metadata, kw_only) 390*cda5da8dSAndroid Build Coastguard Worker 391*cda5da8dSAndroid Build Coastguard Worker 392*cda5da8dSAndroid Build Coastguard Workerdef _fields_in_init_order(fields): 393*cda5da8dSAndroid Build Coastguard Worker # Returns the fields as __init__ will output them. It returns 2 tuples: 394*cda5da8dSAndroid Build Coastguard Worker # the first for normal args, and the second for keyword args. 395*cda5da8dSAndroid Build Coastguard Worker 396*cda5da8dSAndroid Build Coastguard Worker return (tuple(f for f in fields if f.init and not f.kw_only), 397*cda5da8dSAndroid Build Coastguard Worker tuple(f for f in fields if f.init and f.kw_only) 398*cda5da8dSAndroid Build Coastguard Worker ) 399*cda5da8dSAndroid Build Coastguard Worker 400*cda5da8dSAndroid Build Coastguard Worker 401*cda5da8dSAndroid Build Coastguard Workerdef _tuple_str(obj_name, fields): 402*cda5da8dSAndroid Build Coastguard Worker # Return a string representing each field of obj_name as a tuple 403*cda5da8dSAndroid Build Coastguard Worker # member. So, if fields is ['x', 'y'] and obj_name is "self", 404*cda5da8dSAndroid Build Coastguard Worker # return "(self.x,self.y)". 405*cda5da8dSAndroid Build Coastguard Worker 406*cda5da8dSAndroid Build Coastguard Worker # Special case for the 0-tuple. 407*cda5da8dSAndroid Build Coastguard Worker if not fields: 408*cda5da8dSAndroid Build Coastguard Worker return '()' 409*cda5da8dSAndroid Build Coastguard Worker # Note the trailing comma, needed if this turns out to be a 1-tuple. 410*cda5da8dSAndroid Build Coastguard Worker return f'({",".join([f"{obj_name}.{f.name}" for f in fields])},)' 411*cda5da8dSAndroid Build Coastguard Worker 412*cda5da8dSAndroid Build Coastguard Worker 413*cda5da8dSAndroid Build Coastguard Workerdef _create_fn(name, args, body, *, globals=None, locals=None, 414*cda5da8dSAndroid Build Coastguard Worker return_type=MISSING): 415*cda5da8dSAndroid Build Coastguard Worker # Note that we may mutate locals. Callers beware! 416*cda5da8dSAndroid Build Coastguard Worker # The only callers are internal to this module, so no 417*cda5da8dSAndroid Build Coastguard Worker # worries about external callers. 418*cda5da8dSAndroid Build Coastguard Worker if locals is None: 419*cda5da8dSAndroid Build Coastguard Worker locals = {} 420*cda5da8dSAndroid Build Coastguard Worker return_annotation = '' 421*cda5da8dSAndroid Build Coastguard Worker if return_type is not MISSING: 422*cda5da8dSAndroid Build Coastguard Worker locals['_return_type'] = return_type 423*cda5da8dSAndroid Build Coastguard Worker return_annotation = '->_return_type' 424*cda5da8dSAndroid Build Coastguard Worker args = ','.join(args) 425*cda5da8dSAndroid Build Coastguard Worker body = '\n'.join(f' {b}' for b in body) 426*cda5da8dSAndroid Build Coastguard Worker 427*cda5da8dSAndroid Build Coastguard Worker # Compute the text of the entire function. 428*cda5da8dSAndroid Build Coastguard Worker txt = f' def {name}({args}){return_annotation}:\n{body}' 429*cda5da8dSAndroid Build Coastguard Worker 430*cda5da8dSAndroid Build Coastguard Worker local_vars = ', '.join(locals.keys()) 431*cda5da8dSAndroid Build Coastguard Worker txt = f"def __create_fn__({local_vars}):\n{txt}\n return {name}" 432*cda5da8dSAndroid Build Coastguard Worker ns = {} 433*cda5da8dSAndroid Build Coastguard Worker exec(txt, globals, ns) 434*cda5da8dSAndroid Build Coastguard Worker return ns['__create_fn__'](**locals) 435*cda5da8dSAndroid Build Coastguard Worker 436*cda5da8dSAndroid Build Coastguard Worker 437*cda5da8dSAndroid Build Coastguard Workerdef _field_assign(frozen, name, value, self_name): 438*cda5da8dSAndroid Build Coastguard Worker # If we're a frozen class, then assign to our fields in __init__ 439*cda5da8dSAndroid Build Coastguard Worker # via object.__setattr__. Otherwise, just use a simple 440*cda5da8dSAndroid Build Coastguard Worker # assignment. 441*cda5da8dSAndroid Build Coastguard Worker # 442*cda5da8dSAndroid Build Coastguard Worker # self_name is what "self" is called in this function: don't 443*cda5da8dSAndroid Build Coastguard Worker # hard-code "self", since that might be a field name. 444*cda5da8dSAndroid Build Coastguard Worker if frozen: 445*cda5da8dSAndroid Build Coastguard Worker return f'__dataclass_builtins_object__.__setattr__({self_name},{name!r},{value})' 446*cda5da8dSAndroid Build Coastguard Worker return f'{self_name}.{name}={value}' 447*cda5da8dSAndroid Build Coastguard Worker 448*cda5da8dSAndroid Build Coastguard Worker 449*cda5da8dSAndroid Build Coastguard Workerdef _field_init(f, frozen, globals, self_name, slots): 450*cda5da8dSAndroid Build Coastguard Worker # Return the text of the line in the body of __init__ that will 451*cda5da8dSAndroid Build Coastguard Worker # initialize this field. 452*cda5da8dSAndroid Build Coastguard Worker 453*cda5da8dSAndroid Build Coastguard Worker default_name = f'_dflt_{f.name}' 454*cda5da8dSAndroid Build Coastguard Worker if f.default_factory is not MISSING: 455*cda5da8dSAndroid Build Coastguard Worker if f.init: 456*cda5da8dSAndroid Build Coastguard Worker # This field has a default factory. If a parameter is 457*cda5da8dSAndroid Build Coastguard Worker # given, use it. If not, call the factory. 458*cda5da8dSAndroid Build Coastguard Worker globals[default_name] = f.default_factory 459*cda5da8dSAndroid Build Coastguard Worker value = (f'{default_name}() ' 460*cda5da8dSAndroid Build Coastguard Worker f'if {f.name} is _HAS_DEFAULT_FACTORY ' 461*cda5da8dSAndroid Build Coastguard Worker f'else {f.name}') 462*cda5da8dSAndroid Build Coastguard Worker else: 463*cda5da8dSAndroid Build Coastguard Worker # This is a field that's not in the __init__ params, but 464*cda5da8dSAndroid Build Coastguard Worker # has a default factory function. It needs to be 465*cda5da8dSAndroid Build Coastguard Worker # initialized here by calling the factory function, 466*cda5da8dSAndroid Build Coastguard Worker # because there's no other way to initialize it. 467*cda5da8dSAndroid Build Coastguard Worker 468*cda5da8dSAndroid Build Coastguard Worker # For a field initialized with a default=defaultvalue, the 469*cda5da8dSAndroid Build Coastguard Worker # class dict just has the default value 470*cda5da8dSAndroid Build Coastguard Worker # (cls.fieldname=defaultvalue). But that won't work for a 471*cda5da8dSAndroid Build Coastguard Worker # default factory, the factory must be called in __init__ 472*cda5da8dSAndroid Build Coastguard Worker # and we must assign that to self.fieldname. We can't 473*cda5da8dSAndroid Build Coastguard Worker # fall back to the class dict's value, both because it's 474*cda5da8dSAndroid Build Coastguard Worker # not set, and because it might be different per-class 475*cda5da8dSAndroid Build Coastguard Worker # (which, after all, is why we have a factory function!). 476*cda5da8dSAndroid Build Coastguard Worker 477*cda5da8dSAndroid Build Coastguard Worker globals[default_name] = f.default_factory 478*cda5da8dSAndroid Build Coastguard Worker value = f'{default_name}()' 479*cda5da8dSAndroid Build Coastguard Worker else: 480*cda5da8dSAndroid Build Coastguard Worker # No default factory. 481*cda5da8dSAndroid Build Coastguard Worker if f.init: 482*cda5da8dSAndroid Build Coastguard Worker if f.default is MISSING: 483*cda5da8dSAndroid Build Coastguard Worker # There's no default, just do an assignment. 484*cda5da8dSAndroid Build Coastguard Worker value = f.name 485*cda5da8dSAndroid Build Coastguard Worker elif f.default is not MISSING: 486*cda5da8dSAndroid Build Coastguard Worker globals[default_name] = f.default 487*cda5da8dSAndroid Build Coastguard Worker value = f.name 488*cda5da8dSAndroid Build Coastguard Worker else: 489*cda5da8dSAndroid Build Coastguard Worker # If the class has slots, then initialize this field. 490*cda5da8dSAndroid Build Coastguard Worker if slots and f.default is not MISSING: 491*cda5da8dSAndroid Build Coastguard Worker globals[default_name] = f.default 492*cda5da8dSAndroid Build Coastguard Worker value = default_name 493*cda5da8dSAndroid Build Coastguard Worker else: 494*cda5da8dSAndroid Build Coastguard Worker # This field does not need initialization: reading from it will 495*cda5da8dSAndroid Build Coastguard Worker # just use the class attribute that contains the default. 496*cda5da8dSAndroid Build Coastguard Worker # Signify that to the caller by returning None. 497*cda5da8dSAndroid Build Coastguard Worker return None 498*cda5da8dSAndroid Build Coastguard Worker 499*cda5da8dSAndroid Build Coastguard Worker # Only test this now, so that we can create variables for the 500*cda5da8dSAndroid Build Coastguard Worker # default. However, return None to signify that we're not going 501*cda5da8dSAndroid Build Coastguard Worker # to actually do the assignment statement for InitVars. 502*cda5da8dSAndroid Build Coastguard Worker if f._field_type is _FIELD_INITVAR: 503*cda5da8dSAndroid Build Coastguard Worker return None 504*cda5da8dSAndroid Build Coastguard Worker 505*cda5da8dSAndroid Build Coastguard Worker # Now, actually generate the field assignment. 506*cda5da8dSAndroid Build Coastguard Worker return _field_assign(frozen, f.name, value, self_name) 507*cda5da8dSAndroid Build Coastguard Worker 508*cda5da8dSAndroid Build Coastguard Worker 509*cda5da8dSAndroid Build Coastguard Workerdef _init_param(f): 510*cda5da8dSAndroid Build Coastguard Worker # Return the __init__ parameter string for this field. For 511*cda5da8dSAndroid Build Coastguard Worker # example, the equivalent of 'x:int=3' (except instead of 'int', 512*cda5da8dSAndroid Build Coastguard Worker # reference a variable set to int, and instead of '3', reference a 513*cda5da8dSAndroid Build Coastguard Worker # variable set to 3). 514*cda5da8dSAndroid Build Coastguard Worker if f.default is MISSING and f.default_factory is MISSING: 515*cda5da8dSAndroid Build Coastguard Worker # There's no default, and no default_factory, just output the 516*cda5da8dSAndroid Build Coastguard Worker # variable name and type. 517*cda5da8dSAndroid Build Coastguard Worker default = '' 518*cda5da8dSAndroid Build Coastguard Worker elif f.default is not MISSING: 519*cda5da8dSAndroid Build Coastguard Worker # There's a default, this will be the name that's used to look 520*cda5da8dSAndroid Build Coastguard Worker # it up. 521*cda5da8dSAndroid Build Coastguard Worker default = f'=_dflt_{f.name}' 522*cda5da8dSAndroid Build Coastguard Worker elif f.default_factory is not MISSING: 523*cda5da8dSAndroid Build Coastguard Worker # There's a factory function. Set a marker. 524*cda5da8dSAndroid Build Coastguard Worker default = '=_HAS_DEFAULT_FACTORY' 525*cda5da8dSAndroid Build Coastguard Worker return f'{f.name}:_type_{f.name}{default}' 526*cda5da8dSAndroid Build Coastguard Worker 527*cda5da8dSAndroid Build Coastguard Worker 528*cda5da8dSAndroid Build Coastguard Workerdef _init_fn(fields, std_fields, kw_only_fields, frozen, has_post_init, 529*cda5da8dSAndroid Build Coastguard Worker self_name, globals, slots): 530*cda5da8dSAndroid Build Coastguard Worker # fields contains both real fields and InitVar pseudo-fields. 531*cda5da8dSAndroid Build Coastguard Worker 532*cda5da8dSAndroid Build Coastguard Worker # Make sure we don't have fields without defaults following fields 533*cda5da8dSAndroid Build Coastguard Worker # with defaults. This actually would be caught when exec-ing the 534*cda5da8dSAndroid Build Coastguard Worker # function source code, but catching it here gives a better error 535*cda5da8dSAndroid Build Coastguard Worker # message, and future-proofs us in case we build up the function 536*cda5da8dSAndroid Build Coastguard Worker # using ast. 537*cda5da8dSAndroid Build Coastguard Worker 538*cda5da8dSAndroid Build Coastguard Worker seen_default = False 539*cda5da8dSAndroid Build Coastguard Worker for f in std_fields: 540*cda5da8dSAndroid Build Coastguard Worker # Only consider the non-kw-only fields in the __init__ call. 541*cda5da8dSAndroid Build Coastguard Worker if f.init: 542*cda5da8dSAndroid Build Coastguard Worker if not (f.default is MISSING and f.default_factory is MISSING): 543*cda5da8dSAndroid Build Coastguard Worker seen_default = True 544*cda5da8dSAndroid Build Coastguard Worker elif seen_default: 545*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f'non-default argument {f.name!r} ' 546*cda5da8dSAndroid Build Coastguard Worker 'follows default argument') 547*cda5da8dSAndroid Build Coastguard Worker 548*cda5da8dSAndroid Build Coastguard Worker locals = {f'_type_{f.name}': f.type for f in fields} 549*cda5da8dSAndroid Build Coastguard Worker locals.update({ 550*cda5da8dSAndroid Build Coastguard Worker 'MISSING': MISSING, 551*cda5da8dSAndroid Build Coastguard Worker '_HAS_DEFAULT_FACTORY': _HAS_DEFAULT_FACTORY, 552*cda5da8dSAndroid Build Coastguard Worker '__dataclass_builtins_object__': object, 553*cda5da8dSAndroid Build Coastguard Worker }) 554*cda5da8dSAndroid Build Coastguard Worker 555*cda5da8dSAndroid Build Coastguard Worker body_lines = [] 556*cda5da8dSAndroid Build Coastguard Worker for f in fields: 557*cda5da8dSAndroid Build Coastguard Worker line = _field_init(f, frozen, locals, self_name, slots) 558*cda5da8dSAndroid Build Coastguard Worker # line is None means that this field doesn't require 559*cda5da8dSAndroid Build Coastguard Worker # initialization (it's a pseudo-field). Just skip it. 560*cda5da8dSAndroid Build Coastguard Worker if line: 561*cda5da8dSAndroid Build Coastguard Worker body_lines.append(line) 562*cda5da8dSAndroid Build Coastguard Worker 563*cda5da8dSAndroid Build Coastguard Worker # Does this class have a post-init function? 564*cda5da8dSAndroid Build Coastguard Worker if has_post_init: 565*cda5da8dSAndroid Build Coastguard Worker params_str = ','.join(f.name for f in fields 566*cda5da8dSAndroid Build Coastguard Worker if f._field_type is _FIELD_INITVAR) 567*cda5da8dSAndroid Build Coastguard Worker body_lines.append(f'{self_name}.{_POST_INIT_NAME}({params_str})') 568*cda5da8dSAndroid Build Coastguard Worker 569*cda5da8dSAndroid Build Coastguard Worker # If no body lines, use 'pass'. 570*cda5da8dSAndroid Build Coastguard Worker if not body_lines: 571*cda5da8dSAndroid Build Coastguard Worker body_lines = ['pass'] 572*cda5da8dSAndroid Build Coastguard Worker 573*cda5da8dSAndroid Build Coastguard Worker _init_params = [_init_param(f) for f in std_fields] 574*cda5da8dSAndroid Build Coastguard Worker if kw_only_fields: 575*cda5da8dSAndroid Build Coastguard Worker # Add the keyword-only args. Because the * can only be added if 576*cda5da8dSAndroid Build Coastguard Worker # there's at least one keyword-only arg, there needs to be a test here 577*cda5da8dSAndroid Build Coastguard Worker # (instead of just concatenting the lists together). 578*cda5da8dSAndroid Build Coastguard Worker _init_params += ['*'] 579*cda5da8dSAndroid Build Coastguard Worker _init_params += [_init_param(f) for f in kw_only_fields] 580*cda5da8dSAndroid Build Coastguard Worker return _create_fn('__init__', 581*cda5da8dSAndroid Build Coastguard Worker [self_name] + _init_params, 582*cda5da8dSAndroid Build Coastguard Worker body_lines, 583*cda5da8dSAndroid Build Coastguard Worker locals=locals, 584*cda5da8dSAndroid Build Coastguard Worker globals=globals, 585*cda5da8dSAndroid Build Coastguard Worker return_type=None) 586*cda5da8dSAndroid Build Coastguard Worker 587*cda5da8dSAndroid Build Coastguard Worker 588*cda5da8dSAndroid Build Coastguard Workerdef _repr_fn(fields, globals): 589*cda5da8dSAndroid Build Coastguard Worker fn = _create_fn('__repr__', 590*cda5da8dSAndroid Build Coastguard Worker ('self',), 591*cda5da8dSAndroid Build Coastguard Worker ['return self.__class__.__qualname__ + f"(' + 592*cda5da8dSAndroid Build Coastguard Worker ', '.join([f"{f.name}={{self.{f.name}!r}}" 593*cda5da8dSAndroid Build Coastguard Worker for f in fields]) + 594*cda5da8dSAndroid Build Coastguard Worker ')"'], 595*cda5da8dSAndroid Build Coastguard Worker globals=globals) 596*cda5da8dSAndroid Build Coastguard Worker return _recursive_repr(fn) 597*cda5da8dSAndroid Build Coastguard Worker 598*cda5da8dSAndroid Build Coastguard Worker 599*cda5da8dSAndroid Build Coastguard Workerdef _frozen_get_del_attr(cls, fields, globals): 600*cda5da8dSAndroid Build Coastguard Worker locals = {'cls': cls, 601*cda5da8dSAndroid Build Coastguard Worker 'FrozenInstanceError': FrozenInstanceError} 602*cda5da8dSAndroid Build Coastguard Worker if fields: 603*cda5da8dSAndroid Build Coastguard Worker fields_str = '(' + ','.join(repr(f.name) for f in fields) + ',)' 604*cda5da8dSAndroid Build Coastguard Worker else: 605*cda5da8dSAndroid Build Coastguard Worker # Special case for the zero-length tuple. 606*cda5da8dSAndroid Build Coastguard Worker fields_str = '()' 607*cda5da8dSAndroid Build Coastguard Worker return (_create_fn('__setattr__', 608*cda5da8dSAndroid Build Coastguard Worker ('self', 'name', 'value'), 609*cda5da8dSAndroid Build Coastguard Worker (f'if type(self) is cls or name in {fields_str}:', 610*cda5da8dSAndroid Build Coastguard Worker ' raise FrozenInstanceError(f"cannot assign to field {name!r}")', 611*cda5da8dSAndroid Build Coastguard Worker f'super(cls, self).__setattr__(name, value)'), 612*cda5da8dSAndroid Build Coastguard Worker locals=locals, 613*cda5da8dSAndroid Build Coastguard Worker globals=globals), 614*cda5da8dSAndroid Build Coastguard Worker _create_fn('__delattr__', 615*cda5da8dSAndroid Build Coastguard Worker ('self', 'name'), 616*cda5da8dSAndroid Build Coastguard Worker (f'if type(self) is cls or name in {fields_str}:', 617*cda5da8dSAndroid Build Coastguard Worker ' raise FrozenInstanceError(f"cannot delete field {name!r}")', 618*cda5da8dSAndroid Build Coastguard Worker f'super(cls, self).__delattr__(name)'), 619*cda5da8dSAndroid Build Coastguard Worker locals=locals, 620*cda5da8dSAndroid Build Coastguard Worker globals=globals), 621*cda5da8dSAndroid Build Coastguard Worker ) 622*cda5da8dSAndroid Build Coastguard Worker 623*cda5da8dSAndroid Build Coastguard Worker 624*cda5da8dSAndroid Build Coastguard Workerdef _cmp_fn(name, op, self_tuple, other_tuple, globals): 625*cda5da8dSAndroid Build Coastguard Worker # Create a comparison function. If the fields in the object are 626*cda5da8dSAndroid Build Coastguard Worker # named 'x' and 'y', then self_tuple is the string 627*cda5da8dSAndroid Build Coastguard Worker # '(self.x,self.y)' and other_tuple is the string 628*cda5da8dSAndroid Build Coastguard Worker # '(other.x,other.y)'. 629*cda5da8dSAndroid Build Coastguard Worker 630*cda5da8dSAndroid Build Coastguard Worker return _create_fn(name, 631*cda5da8dSAndroid Build Coastguard Worker ('self', 'other'), 632*cda5da8dSAndroid Build Coastguard Worker [ 'if other.__class__ is self.__class__:', 633*cda5da8dSAndroid Build Coastguard Worker f' return {self_tuple}{op}{other_tuple}', 634*cda5da8dSAndroid Build Coastguard Worker 'return NotImplemented'], 635*cda5da8dSAndroid Build Coastguard Worker globals=globals) 636*cda5da8dSAndroid Build Coastguard Worker 637*cda5da8dSAndroid Build Coastguard Worker 638*cda5da8dSAndroid Build Coastguard Workerdef _hash_fn(fields, globals): 639*cda5da8dSAndroid Build Coastguard Worker self_tuple = _tuple_str('self', fields) 640*cda5da8dSAndroid Build Coastguard Worker return _create_fn('__hash__', 641*cda5da8dSAndroid Build Coastguard Worker ('self',), 642*cda5da8dSAndroid Build Coastguard Worker [f'return hash({self_tuple})'], 643*cda5da8dSAndroid Build Coastguard Worker globals=globals) 644*cda5da8dSAndroid Build Coastguard Worker 645*cda5da8dSAndroid Build Coastguard Worker 646*cda5da8dSAndroid Build Coastguard Workerdef _is_classvar(a_type, typing): 647*cda5da8dSAndroid Build Coastguard Worker # This test uses a typing internal class, but it's the best way to 648*cda5da8dSAndroid Build Coastguard Worker # test if this is a ClassVar. 649*cda5da8dSAndroid Build Coastguard Worker return (a_type is typing.ClassVar 650*cda5da8dSAndroid Build Coastguard Worker or (type(a_type) is typing._GenericAlias 651*cda5da8dSAndroid Build Coastguard Worker and a_type.__origin__ is typing.ClassVar)) 652*cda5da8dSAndroid Build Coastguard Worker 653*cda5da8dSAndroid Build Coastguard Worker 654*cda5da8dSAndroid Build Coastguard Workerdef _is_initvar(a_type, dataclasses): 655*cda5da8dSAndroid Build Coastguard Worker # The module we're checking against is the module we're 656*cda5da8dSAndroid Build Coastguard Worker # currently in (dataclasses.py). 657*cda5da8dSAndroid Build Coastguard Worker return (a_type is dataclasses.InitVar 658*cda5da8dSAndroid Build Coastguard Worker or type(a_type) is dataclasses.InitVar) 659*cda5da8dSAndroid Build Coastguard Worker 660*cda5da8dSAndroid Build Coastguard Workerdef _is_kw_only(a_type, dataclasses): 661*cda5da8dSAndroid Build Coastguard Worker return a_type is dataclasses.KW_ONLY 662*cda5da8dSAndroid Build Coastguard Worker 663*cda5da8dSAndroid Build Coastguard Worker 664*cda5da8dSAndroid Build Coastguard Workerdef _is_type(annotation, cls, a_module, a_type, is_type_predicate): 665*cda5da8dSAndroid Build Coastguard Worker # Given a type annotation string, does it refer to a_type in 666*cda5da8dSAndroid Build Coastguard Worker # a_module? For example, when checking that annotation denotes a 667*cda5da8dSAndroid Build Coastguard Worker # ClassVar, then a_module is typing, and a_type is 668*cda5da8dSAndroid Build Coastguard Worker # typing.ClassVar. 669*cda5da8dSAndroid Build Coastguard Worker 670*cda5da8dSAndroid Build Coastguard Worker # It's possible to look up a_module given a_type, but it involves 671*cda5da8dSAndroid Build Coastguard Worker # looking in sys.modules (again!), and seems like a waste since 672*cda5da8dSAndroid Build Coastguard Worker # the caller already knows a_module. 673*cda5da8dSAndroid Build Coastguard Worker 674*cda5da8dSAndroid Build Coastguard Worker # - annotation is a string type annotation 675*cda5da8dSAndroid Build Coastguard Worker # - cls is the class that this annotation was found in 676*cda5da8dSAndroid Build Coastguard Worker # - a_module is the module we want to match 677*cda5da8dSAndroid Build Coastguard Worker # - a_type is the type in that module we want to match 678*cda5da8dSAndroid Build Coastguard Worker # - is_type_predicate is a function called with (obj, a_module) 679*cda5da8dSAndroid Build Coastguard Worker # that determines if obj is of the desired type. 680*cda5da8dSAndroid Build Coastguard Worker 681*cda5da8dSAndroid Build Coastguard Worker # Since this test does not do a local namespace lookup (and 682*cda5da8dSAndroid Build Coastguard Worker # instead only a module (global) lookup), there are some things it 683*cda5da8dSAndroid Build Coastguard Worker # gets wrong. 684*cda5da8dSAndroid Build Coastguard Worker 685*cda5da8dSAndroid Build Coastguard Worker # With string annotations, cv0 will be detected as a ClassVar: 686*cda5da8dSAndroid Build Coastguard Worker # CV = ClassVar 687*cda5da8dSAndroid Build Coastguard Worker # @dataclass 688*cda5da8dSAndroid Build Coastguard Worker # class C0: 689*cda5da8dSAndroid Build Coastguard Worker # cv0: CV 690*cda5da8dSAndroid Build Coastguard Worker 691*cda5da8dSAndroid Build Coastguard Worker # But in this example cv1 will not be detected as a ClassVar: 692*cda5da8dSAndroid Build Coastguard Worker # @dataclass 693*cda5da8dSAndroid Build Coastguard Worker # class C1: 694*cda5da8dSAndroid Build Coastguard Worker # CV = ClassVar 695*cda5da8dSAndroid Build Coastguard Worker # cv1: CV 696*cda5da8dSAndroid Build Coastguard Worker 697*cda5da8dSAndroid Build Coastguard Worker # In C1, the code in this function (_is_type) will look up "CV" in 698*cda5da8dSAndroid Build Coastguard Worker # the module and not find it, so it will not consider cv1 as a 699*cda5da8dSAndroid Build Coastguard Worker # ClassVar. This is a fairly obscure corner case, and the best 700*cda5da8dSAndroid Build Coastguard Worker # way to fix it would be to eval() the string "CV" with the 701*cda5da8dSAndroid Build Coastguard Worker # correct global and local namespaces. However that would involve 702*cda5da8dSAndroid Build Coastguard Worker # a eval() penalty for every single field of every dataclass 703*cda5da8dSAndroid Build Coastguard Worker # that's defined. It was judged not worth it. 704*cda5da8dSAndroid Build Coastguard Worker 705*cda5da8dSAndroid Build Coastguard Worker match = _MODULE_IDENTIFIER_RE.match(annotation) 706*cda5da8dSAndroid Build Coastguard Worker if match: 707*cda5da8dSAndroid Build Coastguard Worker ns = None 708*cda5da8dSAndroid Build Coastguard Worker module_name = match.group(1) 709*cda5da8dSAndroid Build Coastguard Worker if not module_name: 710*cda5da8dSAndroid Build Coastguard Worker # No module name, assume the class's module did 711*cda5da8dSAndroid Build Coastguard Worker # "from dataclasses import InitVar". 712*cda5da8dSAndroid Build Coastguard Worker ns = sys.modules.get(cls.__module__).__dict__ 713*cda5da8dSAndroid Build Coastguard Worker else: 714*cda5da8dSAndroid Build Coastguard Worker # Look up module_name in the class's module. 715*cda5da8dSAndroid Build Coastguard Worker module = sys.modules.get(cls.__module__) 716*cda5da8dSAndroid Build Coastguard Worker if module and module.__dict__.get(module_name) is a_module: 717*cda5da8dSAndroid Build Coastguard Worker ns = sys.modules.get(a_type.__module__).__dict__ 718*cda5da8dSAndroid Build Coastguard Worker if ns and is_type_predicate(ns.get(match.group(2)), a_module): 719*cda5da8dSAndroid Build Coastguard Worker return True 720*cda5da8dSAndroid Build Coastguard Worker return False 721*cda5da8dSAndroid Build Coastguard Worker 722*cda5da8dSAndroid Build Coastguard Worker 723*cda5da8dSAndroid Build Coastguard Workerdef _get_field(cls, a_name, a_type, default_kw_only): 724*cda5da8dSAndroid Build Coastguard Worker # Return a Field object for this field name and type. ClassVars and 725*cda5da8dSAndroid Build Coastguard Worker # InitVars are also returned, but marked as such (see f._field_type). 726*cda5da8dSAndroid Build Coastguard Worker # default_kw_only is the value of kw_only to use if there isn't a field() 727*cda5da8dSAndroid Build Coastguard Worker # that defines it. 728*cda5da8dSAndroid Build Coastguard Worker 729*cda5da8dSAndroid Build Coastguard Worker # If the default value isn't derived from Field, then it's only a 730*cda5da8dSAndroid Build Coastguard Worker # normal default value. Convert it to a Field(). 731*cda5da8dSAndroid Build Coastguard Worker default = getattr(cls, a_name, MISSING) 732*cda5da8dSAndroid Build Coastguard Worker if isinstance(default, Field): 733*cda5da8dSAndroid Build Coastguard Worker f = default 734*cda5da8dSAndroid Build Coastguard Worker else: 735*cda5da8dSAndroid Build Coastguard Worker if isinstance(default, types.MemberDescriptorType): 736*cda5da8dSAndroid Build Coastguard Worker # This is a field in __slots__, so it has no default value. 737*cda5da8dSAndroid Build Coastguard Worker default = MISSING 738*cda5da8dSAndroid Build Coastguard Worker f = field(default=default) 739*cda5da8dSAndroid Build Coastguard Worker 740*cda5da8dSAndroid Build Coastguard Worker # Only at this point do we know the name and the type. Set them. 741*cda5da8dSAndroid Build Coastguard Worker f.name = a_name 742*cda5da8dSAndroid Build Coastguard Worker f.type = a_type 743*cda5da8dSAndroid Build Coastguard Worker 744*cda5da8dSAndroid Build Coastguard Worker # Assume it's a normal field until proven otherwise. We're next 745*cda5da8dSAndroid Build Coastguard Worker # going to decide if it's a ClassVar or InitVar, everything else 746*cda5da8dSAndroid Build Coastguard Worker # is just a normal field. 747*cda5da8dSAndroid Build Coastguard Worker f._field_type = _FIELD 748*cda5da8dSAndroid Build Coastguard Worker 749*cda5da8dSAndroid Build Coastguard Worker # In addition to checking for actual types here, also check for 750*cda5da8dSAndroid Build Coastguard Worker # string annotations. get_type_hints() won't always work for us 751*cda5da8dSAndroid Build Coastguard Worker # (see https://github.com/python/typing/issues/508 for example), 752*cda5da8dSAndroid Build Coastguard Worker # plus it's expensive and would require an eval for every string 753*cda5da8dSAndroid Build Coastguard Worker # annotation. So, make a best effort to see if this is a ClassVar 754*cda5da8dSAndroid Build Coastguard Worker # or InitVar using regex's and checking that the thing referenced 755*cda5da8dSAndroid Build Coastguard Worker # is actually of the correct type. 756*cda5da8dSAndroid Build Coastguard Worker 757*cda5da8dSAndroid Build Coastguard Worker # For the complete discussion, see https://bugs.python.org/issue33453 758*cda5da8dSAndroid Build Coastguard Worker 759*cda5da8dSAndroid Build Coastguard Worker # If typing has not been imported, then it's impossible for any 760*cda5da8dSAndroid Build Coastguard Worker # annotation to be a ClassVar. So, only look for ClassVar if 761*cda5da8dSAndroid Build Coastguard Worker # typing has been imported by any module (not necessarily cls's 762*cda5da8dSAndroid Build Coastguard Worker # module). 763*cda5da8dSAndroid Build Coastguard Worker typing = sys.modules.get('typing') 764*cda5da8dSAndroid Build Coastguard Worker if typing: 765*cda5da8dSAndroid Build Coastguard Worker if (_is_classvar(a_type, typing) 766*cda5da8dSAndroid Build Coastguard Worker or (isinstance(f.type, str) 767*cda5da8dSAndroid Build Coastguard Worker and _is_type(f.type, cls, typing, typing.ClassVar, 768*cda5da8dSAndroid Build Coastguard Worker _is_classvar))): 769*cda5da8dSAndroid Build Coastguard Worker f._field_type = _FIELD_CLASSVAR 770*cda5da8dSAndroid Build Coastguard Worker 771*cda5da8dSAndroid Build Coastguard Worker # If the type is InitVar, or if it's a matching string annotation, 772*cda5da8dSAndroid Build Coastguard Worker # then it's an InitVar. 773*cda5da8dSAndroid Build Coastguard Worker if f._field_type is _FIELD: 774*cda5da8dSAndroid Build Coastguard Worker # The module we're checking against is the module we're 775*cda5da8dSAndroid Build Coastguard Worker # currently in (dataclasses.py). 776*cda5da8dSAndroid Build Coastguard Worker dataclasses = sys.modules[__name__] 777*cda5da8dSAndroid Build Coastguard Worker if (_is_initvar(a_type, dataclasses) 778*cda5da8dSAndroid Build Coastguard Worker or (isinstance(f.type, str) 779*cda5da8dSAndroid Build Coastguard Worker and _is_type(f.type, cls, dataclasses, dataclasses.InitVar, 780*cda5da8dSAndroid Build Coastguard Worker _is_initvar))): 781*cda5da8dSAndroid Build Coastguard Worker f._field_type = _FIELD_INITVAR 782*cda5da8dSAndroid Build Coastguard Worker 783*cda5da8dSAndroid Build Coastguard Worker # Validations for individual fields. This is delayed until now, 784*cda5da8dSAndroid Build Coastguard Worker # instead of in the Field() constructor, since only here do we 785*cda5da8dSAndroid Build Coastguard Worker # know the field name, which allows for better error reporting. 786*cda5da8dSAndroid Build Coastguard Worker 787*cda5da8dSAndroid Build Coastguard Worker # Special restrictions for ClassVar and InitVar. 788*cda5da8dSAndroid Build Coastguard Worker if f._field_type in (_FIELD_CLASSVAR, _FIELD_INITVAR): 789*cda5da8dSAndroid Build Coastguard Worker if f.default_factory is not MISSING: 790*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f'field {f.name} cannot have a ' 791*cda5da8dSAndroid Build Coastguard Worker 'default factory') 792*cda5da8dSAndroid Build Coastguard Worker # Should I check for other field settings? default_factory 793*cda5da8dSAndroid Build Coastguard Worker # seems the most serious to check for. Maybe add others. For 794*cda5da8dSAndroid Build Coastguard Worker # example, how about init=False (or really, 795*cda5da8dSAndroid Build Coastguard Worker # init=<not-the-default-init-value>)? It makes no sense for 796*cda5da8dSAndroid Build Coastguard Worker # ClassVar and InitVar to specify init=<anything>. 797*cda5da8dSAndroid Build Coastguard Worker 798*cda5da8dSAndroid Build Coastguard Worker # kw_only validation and assignment. 799*cda5da8dSAndroid Build Coastguard Worker if f._field_type in (_FIELD, _FIELD_INITVAR): 800*cda5da8dSAndroid Build Coastguard Worker # For real and InitVar fields, if kw_only wasn't specified use the 801*cda5da8dSAndroid Build Coastguard Worker # default value. 802*cda5da8dSAndroid Build Coastguard Worker if f.kw_only is MISSING: 803*cda5da8dSAndroid Build Coastguard Worker f.kw_only = default_kw_only 804*cda5da8dSAndroid Build Coastguard Worker else: 805*cda5da8dSAndroid Build Coastguard Worker # Make sure kw_only isn't set for ClassVars 806*cda5da8dSAndroid Build Coastguard Worker assert f._field_type is _FIELD_CLASSVAR 807*cda5da8dSAndroid Build Coastguard Worker if f.kw_only is not MISSING: 808*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f'field {f.name} is a ClassVar but specifies ' 809*cda5da8dSAndroid Build Coastguard Worker 'kw_only') 810*cda5da8dSAndroid Build Coastguard Worker 811*cda5da8dSAndroid Build Coastguard Worker # For real fields, disallow mutable defaults. Use unhashable as a proxy 812*cda5da8dSAndroid Build Coastguard Worker # indicator for mutability. Read the __hash__ attribute from the class, 813*cda5da8dSAndroid Build Coastguard Worker # not the instance. 814*cda5da8dSAndroid Build Coastguard Worker if f._field_type is _FIELD and f.default.__class__.__hash__ is None: 815*cda5da8dSAndroid Build Coastguard Worker raise ValueError(f'mutable default {type(f.default)} for field ' 816*cda5da8dSAndroid Build Coastguard Worker f'{f.name} is not allowed: use default_factory') 817*cda5da8dSAndroid Build Coastguard Worker 818*cda5da8dSAndroid Build Coastguard Worker return f 819*cda5da8dSAndroid Build Coastguard Worker 820*cda5da8dSAndroid Build Coastguard Workerdef _set_qualname(cls, value): 821*cda5da8dSAndroid Build Coastguard Worker # Ensure that the functions returned from _create_fn uses the proper 822*cda5da8dSAndroid Build Coastguard Worker # __qualname__ (the class they belong to). 823*cda5da8dSAndroid Build Coastguard Worker if isinstance(value, FunctionType): 824*cda5da8dSAndroid Build Coastguard Worker value.__qualname__ = f"{cls.__qualname__}.{value.__name__}" 825*cda5da8dSAndroid Build Coastguard Worker return value 826*cda5da8dSAndroid Build Coastguard Worker 827*cda5da8dSAndroid Build Coastguard Workerdef _set_new_attribute(cls, name, value): 828*cda5da8dSAndroid Build Coastguard Worker # Never overwrites an existing attribute. Returns True if the 829*cda5da8dSAndroid Build Coastguard Worker # attribute already exists. 830*cda5da8dSAndroid Build Coastguard Worker if name in cls.__dict__: 831*cda5da8dSAndroid Build Coastguard Worker return True 832*cda5da8dSAndroid Build Coastguard Worker _set_qualname(cls, value) 833*cda5da8dSAndroid Build Coastguard Worker setattr(cls, name, value) 834*cda5da8dSAndroid Build Coastguard Worker return False 835*cda5da8dSAndroid Build Coastguard Worker 836*cda5da8dSAndroid Build Coastguard Worker 837*cda5da8dSAndroid Build Coastguard Worker# Decide if/how we're going to create a hash function. Key is 838*cda5da8dSAndroid Build Coastguard Worker# (unsafe_hash, eq, frozen, does-hash-exist). Value is the action to 839*cda5da8dSAndroid Build Coastguard Worker# take. The common case is to do nothing, so instead of providing a 840*cda5da8dSAndroid Build Coastguard Worker# function that is a no-op, use None to signify that. 841*cda5da8dSAndroid Build Coastguard Worker 842*cda5da8dSAndroid Build Coastguard Workerdef _hash_set_none(cls, fields, globals): 843*cda5da8dSAndroid Build Coastguard Worker return None 844*cda5da8dSAndroid Build Coastguard Worker 845*cda5da8dSAndroid Build Coastguard Workerdef _hash_add(cls, fields, globals): 846*cda5da8dSAndroid Build Coastguard Worker flds = [f for f in fields if (f.compare if f.hash is None else f.hash)] 847*cda5da8dSAndroid Build Coastguard Worker return _set_qualname(cls, _hash_fn(flds, globals)) 848*cda5da8dSAndroid Build Coastguard Worker 849*cda5da8dSAndroid Build Coastguard Workerdef _hash_exception(cls, fields, globals): 850*cda5da8dSAndroid Build Coastguard Worker # Raise an exception. 851*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f'Cannot overwrite attribute __hash__ ' 852*cda5da8dSAndroid Build Coastguard Worker f'in class {cls.__name__}') 853*cda5da8dSAndroid Build Coastguard Worker 854*cda5da8dSAndroid Build Coastguard Worker# 855*cda5da8dSAndroid Build Coastguard Worker# +-------------------------------------- unsafe_hash? 856*cda5da8dSAndroid Build Coastguard Worker# | +------------------------------- eq? 857*cda5da8dSAndroid Build Coastguard Worker# | | +------------------------ frozen? 858*cda5da8dSAndroid Build Coastguard Worker# | | | +---------------- has-explicit-hash? 859*cda5da8dSAndroid Build Coastguard Worker# | | | | 860*cda5da8dSAndroid Build Coastguard Worker# | | | | +------- action 861*cda5da8dSAndroid Build Coastguard Worker# | | | | | 862*cda5da8dSAndroid Build Coastguard Worker# v v v v v 863*cda5da8dSAndroid Build Coastguard Worker_hash_action = {(False, False, False, False): None, 864*cda5da8dSAndroid Build Coastguard Worker (False, False, False, True ): None, 865*cda5da8dSAndroid Build Coastguard Worker (False, False, True, False): None, 866*cda5da8dSAndroid Build Coastguard Worker (False, False, True, True ): None, 867*cda5da8dSAndroid Build Coastguard Worker (False, True, False, False): _hash_set_none, 868*cda5da8dSAndroid Build Coastguard Worker (False, True, False, True ): None, 869*cda5da8dSAndroid Build Coastguard Worker (False, True, True, False): _hash_add, 870*cda5da8dSAndroid Build Coastguard Worker (False, True, True, True ): None, 871*cda5da8dSAndroid Build Coastguard Worker (True, False, False, False): _hash_add, 872*cda5da8dSAndroid Build Coastguard Worker (True, False, False, True ): _hash_exception, 873*cda5da8dSAndroid Build Coastguard Worker (True, False, True, False): _hash_add, 874*cda5da8dSAndroid Build Coastguard Worker (True, False, True, True ): _hash_exception, 875*cda5da8dSAndroid Build Coastguard Worker (True, True, False, False): _hash_add, 876*cda5da8dSAndroid Build Coastguard Worker (True, True, False, True ): _hash_exception, 877*cda5da8dSAndroid Build Coastguard Worker (True, True, True, False): _hash_add, 878*cda5da8dSAndroid Build Coastguard Worker (True, True, True, True ): _hash_exception, 879*cda5da8dSAndroid Build Coastguard Worker } 880*cda5da8dSAndroid Build Coastguard Worker# See https://bugs.python.org/issue32929#msg312829 for an if-statement 881*cda5da8dSAndroid Build Coastguard Worker# version of this table. 882*cda5da8dSAndroid Build Coastguard Worker 883*cda5da8dSAndroid Build Coastguard Worker 884*cda5da8dSAndroid Build Coastguard Workerdef _process_class(cls, init, repr, eq, order, unsafe_hash, frozen, 885*cda5da8dSAndroid Build Coastguard Worker match_args, kw_only, slots, weakref_slot): 886*cda5da8dSAndroid Build Coastguard Worker # Now that dicts retain insertion order, there's no reason to use 887*cda5da8dSAndroid Build Coastguard Worker # an ordered dict. I am leveraging that ordering here, because 888*cda5da8dSAndroid Build Coastguard Worker # derived class fields overwrite base class fields, but the order 889*cda5da8dSAndroid Build Coastguard Worker # is defined by the base class, which is found first. 890*cda5da8dSAndroid Build Coastguard Worker fields = {} 891*cda5da8dSAndroid Build Coastguard Worker 892*cda5da8dSAndroid Build Coastguard Worker if cls.__module__ in sys.modules: 893*cda5da8dSAndroid Build Coastguard Worker globals = sys.modules[cls.__module__].__dict__ 894*cda5da8dSAndroid Build Coastguard Worker else: 895*cda5da8dSAndroid Build Coastguard Worker # Theoretically this can happen if someone writes 896*cda5da8dSAndroid Build Coastguard Worker # a custom string to cls.__module__. In which case 897*cda5da8dSAndroid Build Coastguard Worker # such dataclass won't be fully introspectable 898*cda5da8dSAndroid Build Coastguard Worker # (w.r.t. typing.get_type_hints) but will still function 899*cda5da8dSAndroid Build Coastguard Worker # correctly. 900*cda5da8dSAndroid Build Coastguard Worker globals = {} 901*cda5da8dSAndroid Build Coastguard Worker 902*cda5da8dSAndroid Build Coastguard Worker setattr(cls, _PARAMS, _DataclassParams(init, repr, eq, order, 903*cda5da8dSAndroid Build Coastguard Worker unsafe_hash, frozen)) 904*cda5da8dSAndroid Build Coastguard Worker 905*cda5da8dSAndroid Build Coastguard Worker # Find our base classes in reverse MRO order, and exclude 906*cda5da8dSAndroid Build Coastguard Worker # ourselves. In reversed order so that more derived classes 907*cda5da8dSAndroid Build Coastguard Worker # override earlier field definitions in base classes. As long as 908*cda5da8dSAndroid Build Coastguard Worker # we're iterating over them, see if any are frozen. 909*cda5da8dSAndroid Build Coastguard Worker any_frozen_base = False 910*cda5da8dSAndroid Build Coastguard Worker has_dataclass_bases = False 911*cda5da8dSAndroid Build Coastguard Worker for b in cls.__mro__[-1:0:-1]: 912*cda5da8dSAndroid Build Coastguard Worker # Only process classes that have been processed by our 913*cda5da8dSAndroid Build Coastguard Worker # decorator. That is, they have a _FIELDS attribute. 914*cda5da8dSAndroid Build Coastguard Worker base_fields = getattr(b, _FIELDS, None) 915*cda5da8dSAndroid Build Coastguard Worker if base_fields is not None: 916*cda5da8dSAndroid Build Coastguard Worker has_dataclass_bases = True 917*cda5da8dSAndroid Build Coastguard Worker for f in base_fields.values(): 918*cda5da8dSAndroid Build Coastguard Worker fields[f.name] = f 919*cda5da8dSAndroid Build Coastguard Worker if getattr(b, _PARAMS).frozen: 920*cda5da8dSAndroid Build Coastguard Worker any_frozen_base = True 921*cda5da8dSAndroid Build Coastguard Worker 922*cda5da8dSAndroid Build Coastguard Worker # Annotations that are defined in this class (not in base 923*cda5da8dSAndroid Build Coastguard Worker # classes). If __annotations__ isn't present, then this class 924*cda5da8dSAndroid Build Coastguard Worker # adds no new annotations. We use this to compute fields that are 925*cda5da8dSAndroid Build Coastguard Worker # added by this class. 926*cda5da8dSAndroid Build Coastguard Worker # 927*cda5da8dSAndroid Build Coastguard Worker # Fields are found from cls_annotations, which is guaranteed to be 928*cda5da8dSAndroid Build Coastguard Worker # ordered. Default values are from class attributes, if a field 929*cda5da8dSAndroid Build Coastguard Worker # has a default. If the default value is a Field(), then it 930*cda5da8dSAndroid Build Coastguard Worker # contains additional info beyond (and possibly including) the 931*cda5da8dSAndroid Build Coastguard Worker # actual default value. Pseudo-fields ClassVars and InitVars are 932*cda5da8dSAndroid Build Coastguard Worker # included, despite the fact that they're not real fields. That's 933*cda5da8dSAndroid Build Coastguard Worker # dealt with later. 934*cda5da8dSAndroid Build Coastguard Worker cls_annotations = cls.__dict__.get('__annotations__', {}) 935*cda5da8dSAndroid Build Coastguard Worker 936*cda5da8dSAndroid Build Coastguard Worker # Now find fields in our class. While doing so, validate some 937*cda5da8dSAndroid Build Coastguard Worker # things, and set the default values (as class attributes) where 938*cda5da8dSAndroid Build Coastguard Worker # we can. 939*cda5da8dSAndroid Build Coastguard Worker cls_fields = [] 940*cda5da8dSAndroid Build Coastguard Worker # Get a reference to this module for the _is_kw_only() test. 941*cda5da8dSAndroid Build Coastguard Worker KW_ONLY_seen = False 942*cda5da8dSAndroid Build Coastguard Worker dataclasses = sys.modules[__name__] 943*cda5da8dSAndroid Build Coastguard Worker for name, type in cls_annotations.items(): 944*cda5da8dSAndroid Build Coastguard Worker # See if this is a marker to change the value of kw_only. 945*cda5da8dSAndroid Build Coastguard Worker if (_is_kw_only(type, dataclasses) 946*cda5da8dSAndroid Build Coastguard Worker or (isinstance(type, str) 947*cda5da8dSAndroid Build Coastguard Worker and _is_type(type, cls, dataclasses, dataclasses.KW_ONLY, 948*cda5da8dSAndroid Build Coastguard Worker _is_kw_only))): 949*cda5da8dSAndroid Build Coastguard Worker # Switch the default to kw_only=True, and ignore this 950*cda5da8dSAndroid Build Coastguard Worker # annotation: it's not a real field. 951*cda5da8dSAndroid Build Coastguard Worker if KW_ONLY_seen: 952*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f'{name!r} is KW_ONLY, but KW_ONLY ' 953*cda5da8dSAndroid Build Coastguard Worker 'has already been specified') 954*cda5da8dSAndroid Build Coastguard Worker KW_ONLY_seen = True 955*cda5da8dSAndroid Build Coastguard Worker kw_only = True 956*cda5da8dSAndroid Build Coastguard Worker else: 957*cda5da8dSAndroid Build Coastguard Worker # Otherwise it's a field of some type. 958*cda5da8dSAndroid Build Coastguard Worker cls_fields.append(_get_field(cls, name, type, kw_only)) 959*cda5da8dSAndroid Build Coastguard Worker 960*cda5da8dSAndroid Build Coastguard Worker for f in cls_fields: 961*cda5da8dSAndroid Build Coastguard Worker fields[f.name] = f 962*cda5da8dSAndroid Build Coastguard Worker 963*cda5da8dSAndroid Build Coastguard Worker # If the class attribute (which is the default value for this 964*cda5da8dSAndroid Build Coastguard Worker # field) exists and is of type 'Field', replace it with the 965*cda5da8dSAndroid Build Coastguard Worker # real default. This is so that normal class introspection 966*cda5da8dSAndroid Build Coastguard Worker # sees a real default value, not a Field. 967*cda5da8dSAndroid Build Coastguard Worker if isinstance(getattr(cls, f.name, None), Field): 968*cda5da8dSAndroid Build Coastguard Worker if f.default is MISSING: 969*cda5da8dSAndroid Build Coastguard Worker # If there's no default, delete the class attribute. 970*cda5da8dSAndroid Build Coastguard Worker # This happens if we specify field(repr=False), for 971*cda5da8dSAndroid Build Coastguard Worker # example (that is, we specified a field object, but 972*cda5da8dSAndroid Build Coastguard Worker # no default value). Also if we're using a default 973*cda5da8dSAndroid Build Coastguard Worker # factory. The class attribute should not be set at 974*cda5da8dSAndroid Build Coastguard Worker # all in the post-processed class. 975*cda5da8dSAndroid Build Coastguard Worker delattr(cls, f.name) 976*cda5da8dSAndroid Build Coastguard Worker else: 977*cda5da8dSAndroid Build Coastguard Worker setattr(cls, f.name, f.default) 978*cda5da8dSAndroid Build Coastguard Worker 979*cda5da8dSAndroid Build Coastguard Worker # Do we have any Field members that don't also have annotations? 980*cda5da8dSAndroid Build Coastguard Worker for name, value in cls.__dict__.items(): 981*cda5da8dSAndroid Build Coastguard Worker if isinstance(value, Field) and not name in cls_annotations: 982*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f'{name!r} is a field but has no type annotation') 983*cda5da8dSAndroid Build Coastguard Worker 984*cda5da8dSAndroid Build Coastguard Worker # Check rules that apply if we are derived from any dataclasses. 985*cda5da8dSAndroid Build Coastguard Worker if has_dataclass_bases: 986*cda5da8dSAndroid Build Coastguard Worker # Raise an exception if any of our bases are frozen, but we're not. 987*cda5da8dSAndroid Build Coastguard Worker if any_frozen_base and not frozen: 988*cda5da8dSAndroid Build Coastguard Worker raise TypeError('cannot inherit non-frozen dataclass from a ' 989*cda5da8dSAndroid Build Coastguard Worker 'frozen one') 990*cda5da8dSAndroid Build Coastguard Worker 991*cda5da8dSAndroid Build Coastguard Worker # Raise an exception if we're frozen, but none of our bases are. 992*cda5da8dSAndroid Build Coastguard Worker if not any_frozen_base and frozen: 993*cda5da8dSAndroid Build Coastguard Worker raise TypeError('cannot inherit frozen dataclass from a ' 994*cda5da8dSAndroid Build Coastguard Worker 'non-frozen one') 995*cda5da8dSAndroid Build Coastguard Worker 996*cda5da8dSAndroid Build Coastguard Worker # Remember all of the fields on our class (including bases). This 997*cda5da8dSAndroid Build Coastguard Worker # also marks this class as being a dataclass. 998*cda5da8dSAndroid Build Coastguard Worker setattr(cls, _FIELDS, fields) 999*cda5da8dSAndroid Build Coastguard Worker 1000*cda5da8dSAndroid Build Coastguard Worker # Was this class defined with an explicit __hash__? Note that if 1001*cda5da8dSAndroid Build Coastguard Worker # __eq__ is defined in this class, then python will automatically 1002*cda5da8dSAndroid Build Coastguard Worker # set __hash__ to None. This is a heuristic, as it's possible 1003*cda5da8dSAndroid Build Coastguard Worker # that such a __hash__ == None was not auto-generated, but it 1004*cda5da8dSAndroid Build Coastguard Worker # close enough. 1005*cda5da8dSAndroid Build Coastguard Worker class_hash = cls.__dict__.get('__hash__', MISSING) 1006*cda5da8dSAndroid Build Coastguard Worker has_explicit_hash = not (class_hash is MISSING or 1007*cda5da8dSAndroid Build Coastguard Worker (class_hash is None and '__eq__' in cls.__dict__)) 1008*cda5da8dSAndroid Build Coastguard Worker 1009*cda5da8dSAndroid Build Coastguard Worker # If we're generating ordering methods, we must be generating the 1010*cda5da8dSAndroid Build Coastguard Worker # eq methods. 1011*cda5da8dSAndroid Build Coastguard Worker if order and not eq: 1012*cda5da8dSAndroid Build Coastguard Worker raise ValueError('eq must be true if order is true') 1013*cda5da8dSAndroid Build Coastguard Worker 1014*cda5da8dSAndroid Build Coastguard Worker # Include InitVars and regular fields (so, not ClassVars). This is 1015*cda5da8dSAndroid Build Coastguard Worker # initialized here, outside of the "if init:" test, because std_init_fields 1016*cda5da8dSAndroid Build Coastguard Worker # is used with match_args, below. 1017*cda5da8dSAndroid Build Coastguard Worker all_init_fields = [f for f in fields.values() 1018*cda5da8dSAndroid Build Coastguard Worker if f._field_type in (_FIELD, _FIELD_INITVAR)] 1019*cda5da8dSAndroid Build Coastguard Worker (std_init_fields, 1020*cda5da8dSAndroid Build Coastguard Worker kw_only_init_fields) = _fields_in_init_order(all_init_fields) 1021*cda5da8dSAndroid Build Coastguard Worker 1022*cda5da8dSAndroid Build Coastguard Worker if init: 1023*cda5da8dSAndroid Build Coastguard Worker # Does this class have a post-init function? 1024*cda5da8dSAndroid Build Coastguard Worker has_post_init = hasattr(cls, _POST_INIT_NAME) 1025*cda5da8dSAndroid Build Coastguard Worker 1026*cda5da8dSAndroid Build Coastguard Worker _set_new_attribute(cls, '__init__', 1027*cda5da8dSAndroid Build Coastguard Worker _init_fn(all_init_fields, 1028*cda5da8dSAndroid Build Coastguard Worker std_init_fields, 1029*cda5da8dSAndroid Build Coastguard Worker kw_only_init_fields, 1030*cda5da8dSAndroid Build Coastguard Worker frozen, 1031*cda5da8dSAndroid Build Coastguard Worker has_post_init, 1032*cda5da8dSAndroid Build Coastguard Worker # The name to use for the "self" 1033*cda5da8dSAndroid Build Coastguard Worker # param in __init__. Use "self" 1034*cda5da8dSAndroid Build Coastguard Worker # if possible. 1035*cda5da8dSAndroid Build Coastguard Worker '__dataclass_self__' if 'self' in fields 1036*cda5da8dSAndroid Build Coastguard Worker else 'self', 1037*cda5da8dSAndroid Build Coastguard Worker globals, 1038*cda5da8dSAndroid Build Coastguard Worker slots, 1039*cda5da8dSAndroid Build Coastguard Worker )) 1040*cda5da8dSAndroid Build Coastguard Worker 1041*cda5da8dSAndroid Build Coastguard Worker # Get the fields as a list, and include only real fields. This is 1042*cda5da8dSAndroid Build Coastguard Worker # used in all of the following methods. 1043*cda5da8dSAndroid Build Coastguard Worker field_list = [f for f in fields.values() if f._field_type is _FIELD] 1044*cda5da8dSAndroid Build Coastguard Worker 1045*cda5da8dSAndroid Build Coastguard Worker if repr: 1046*cda5da8dSAndroid Build Coastguard Worker flds = [f for f in field_list if f.repr] 1047*cda5da8dSAndroid Build Coastguard Worker _set_new_attribute(cls, '__repr__', _repr_fn(flds, globals)) 1048*cda5da8dSAndroid Build Coastguard Worker 1049*cda5da8dSAndroid Build Coastguard Worker if eq: 1050*cda5da8dSAndroid Build Coastguard Worker # Create __eq__ method. There's no need for a __ne__ method, 1051*cda5da8dSAndroid Build Coastguard Worker # since python will call __eq__ and negate it. 1052*cda5da8dSAndroid Build Coastguard Worker flds = [f for f in field_list if f.compare] 1053*cda5da8dSAndroid Build Coastguard Worker self_tuple = _tuple_str('self', flds) 1054*cda5da8dSAndroid Build Coastguard Worker other_tuple = _tuple_str('other', flds) 1055*cda5da8dSAndroid Build Coastguard Worker _set_new_attribute(cls, '__eq__', 1056*cda5da8dSAndroid Build Coastguard Worker _cmp_fn('__eq__', '==', 1057*cda5da8dSAndroid Build Coastguard Worker self_tuple, other_tuple, 1058*cda5da8dSAndroid Build Coastguard Worker globals=globals)) 1059*cda5da8dSAndroid Build Coastguard Worker 1060*cda5da8dSAndroid Build Coastguard Worker if order: 1061*cda5da8dSAndroid Build Coastguard Worker # Create and set the ordering methods. 1062*cda5da8dSAndroid Build Coastguard Worker flds = [f for f in field_list if f.compare] 1063*cda5da8dSAndroid Build Coastguard Worker self_tuple = _tuple_str('self', flds) 1064*cda5da8dSAndroid Build Coastguard Worker other_tuple = _tuple_str('other', flds) 1065*cda5da8dSAndroid Build Coastguard Worker for name, op in [('__lt__', '<'), 1066*cda5da8dSAndroid Build Coastguard Worker ('__le__', '<='), 1067*cda5da8dSAndroid Build Coastguard Worker ('__gt__', '>'), 1068*cda5da8dSAndroid Build Coastguard Worker ('__ge__', '>='), 1069*cda5da8dSAndroid Build Coastguard Worker ]: 1070*cda5da8dSAndroid Build Coastguard Worker if _set_new_attribute(cls, name, 1071*cda5da8dSAndroid Build Coastguard Worker _cmp_fn(name, op, self_tuple, other_tuple, 1072*cda5da8dSAndroid Build Coastguard Worker globals=globals)): 1073*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f'Cannot overwrite attribute {name} ' 1074*cda5da8dSAndroid Build Coastguard Worker f'in class {cls.__name__}. Consider using ' 1075*cda5da8dSAndroid Build Coastguard Worker 'functools.total_ordering') 1076*cda5da8dSAndroid Build Coastguard Worker 1077*cda5da8dSAndroid Build Coastguard Worker if frozen: 1078*cda5da8dSAndroid Build Coastguard Worker for fn in _frozen_get_del_attr(cls, field_list, globals): 1079*cda5da8dSAndroid Build Coastguard Worker if _set_new_attribute(cls, fn.__name__, fn): 1080*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f'Cannot overwrite attribute {fn.__name__} ' 1081*cda5da8dSAndroid Build Coastguard Worker f'in class {cls.__name__}') 1082*cda5da8dSAndroid Build Coastguard Worker 1083*cda5da8dSAndroid Build Coastguard Worker # Decide if/how we're going to create a hash function. 1084*cda5da8dSAndroid Build Coastguard Worker hash_action = _hash_action[bool(unsafe_hash), 1085*cda5da8dSAndroid Build Coastguard Worker bool(eq), 1086*cda5da8dSAndroid Build Coastguard Worker bool(frozen), 1087*cda5da8dSAndroid Build Coastguard Worker has_explicit_hash] 1088*cda5da8dSAndroid Build Coastguard Worker if hash_action: 1089*cda5da8dSAndroid Build Coastguard Worker # No need to call _set_new_attribute here, since by the time 1090*cda5da8dSAndroid Build Coastguard Worker # we're here the overwriting is unconditional. 1091*cda5da8dSAndroid Build Coastguard Worker cls.__hash__ = hash_action(cls, field_list, globals) 1092*cda5da8dSAndroid Build Coastguard Worker 1093*cda5da8dSAndroid Build Coastguard Worker if not getattr(cls, '__doc__'): 1094*cda5da8dSAndroid Build Coastguard Worker # Create a class doc-string. 1095*cda5da8dSAndroid Build Coastguard Worker try: 1096*cda5da8dSAndroid Build Coastguard Worker # In some cases fetching a signature is not possible. 1097*cda5da8dSAndroid Build Coastguard Worker # But, we surely should not fail in this case. 1098*cda5da8dSAndroid Build Coastguard Worker text_sig = str(inspect.signature(cls)).replace(' -> None', '') 1099*cda5da8dSAndroid Build Coastguard Worker except (TypeError, ValueError): 1100*cda5da8dSAndroid Build Coastguard Worker text_sig = '' 1101*cda5da8dSAndroid Build Coastguard Worker cls.__doc__ = (cls.__name__ + text_sig) 1102*cda5da8dSAndroid Build Coastguard Worker 1103*cda5da8dSAndroid Build Coastguard Worker if match_args: 1104*cda5da8dSAndroid Build Coastguard Worker # I could probably compute this once 1105*cda5da8dSAndroid Build Coastguard Worker _set_new_attribute(cls, '__match_args__', 1106*cda5da8dSAndroid Build Coastguard Worker tuple(f.name for f in std_init_fields)) 1107*cda5da8dSAndroid Build Coastguard Worker 1108*cda5da8dSAndroid Build Coastguard Worker # It's an error to specify weakref_slot if slots is False. 1109*cda5da8dSAndroid Build Coastguard Worker if weakref_slot and not slots: 1110*cda5da8dSAndroid Build Coastguard Worker raise TypeError('weakref_slot is True but slots is False') 1111*cda5da8dSAndroid Build Coastguard Worker if slots: 1112*cda5da8dSAndroid Build Coastguard Worker cls = _add_slots(cls, frozen, weakref_slot) 1113*cda5da8dSAndroid Build Coastguard Worker 1114*cda5da8dSAndroid Build Coastguard Worker abc.update_abstractmethods(cls) 1115*cda5da8dSAndroid Build Coastguard Worker 1116*cda5da8dSAndroid Build Coastguard Worker return cls 1117*cda5da8dSAndroid Build Coastguard Worker 1118*cda5da8dSAndroid Build Coastguard Worker 1119*cda5da8dSAndroid Build Coastguard Worker# _dataclass_getstate and _dataclass_setstate are needed for pickling frozen 1120*cda5da8dSAndroid Build Coastguard Worker# classes with slots. These could be slightly more performant if we generated 1121*cda5da8dSAndroid Build Coastguard Worker# the code instead of iterating over fields. But that can be a project for 1122*cda5da8dSAndroid Build Coastguard Worker# another day, if performance becomes an issue. 1123*cda5da8dSAndroid Build Coastguard Workerdef _dataclass_getstate(self): 1124*cda5da8dSAndroid Build Coastguard Worker return [getattr(self, f.name) for f in fields(self)] 1125*cda5da8dSAndroid Build Coastguard Worker 1126*cda5da8dSAndroid Build Coastguard Worker 1127*cda5da8dSAndroid Build Coastguard Workerdef _dataclass_setstate(self, state): 1128*cda5da8dSAndroid Build Coastguard Worker for field, value in zip(fields(self), state): 1129*cda5da8dSAndroid Build Coastguard Worker # use setattr because dataclass may be frozen 1130*cda5da8dSAndroid Build Coastguard Worker object.__setattr__(self, field.name, value) 1131*cda5da8dSAndroid Build Coastguard Worker 1132*cda5da8dSAndroid Build Coastguard Worker 1133*cda5da8dSAndroid Build Coastguard Workerdef _get_slots(cls): 1134*cda5da8dSAndroid Build Coastguard Worker match cls.__dict__.get('__slots__'): 1135*cda5da8dSAndroid Build Coastguard Worker case None: 1136*cda5da8dSAndroid Build Coastguard Worker return 1137*cda5da8dSAndroid Build Coastguard Worker case str(slot): 1138*cda5da8dSAndroid Build Coastguard Worker yield slot 1139*cda5da8dSAndroid Build Coastguard Worker # Slots may be any iterable, but we cannot handle an iterator 1140*cda5da8dSAndroid Build Coastguard Worker # because it will already be (partially) consumed. 1141*cda5da8dSAndroid Build Coastguard Worker case iterable if not hasattr(iterable, '__next__'): 1142*cda5da8dSAndroid Build Coastguard Worker yield from iterable 1143*cda5da8dSAndroid Build Coastguard Worker case _: 1144*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f"Slots of '{cls.__name__}' cannot be determined") 1145*cda5da8dSAndroid Build Coastguard Worker 1146*cda5da8dSAndroid Build Coastguard Worker 1147*cda5da8dSAndroid Build Coastguard Workerdef _add_slots(cls, is_frozen, weakref_slot): 1148*cda5da8dSAndroid Build Coastguard Worker # Need to create a new class, since we can't set __slots__ 1149*cda5da8dSAndroid Build Coastguard Worker # after a class has been created. 1150*cda5da8dSAndroid Build Coastguard Worker 1151*cda5da8dSAndroid Build Coastguard Worker # Make sure __slots__ isn't already set. 1152*cda5da8dSAndroid Build Coastguard Worker if '__slots__' in cls.__dict__: 1153*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f'{cls.__name__} already specifies __slots__') 1154*cda5da8dSAndroid Build Coastguard Worker 1155*cda5da8dSAndroid Build Coastguard Worker # Create a new dict for our new class. 1156*cda5da8dSAndroid Build Coastguard Worker cls_dict = dict(cls.__dict__) 1157*cda5da8dSAndroid Build Coastguard Worker field_names = tuple(f.name for f in fields(cls)) 1158*cda5da8dSAndroid Build Coastguard Worker # Make sure slots don't overlap with those in base classes. 1159*cda5da8dSAndroid Build Coastguard Worker inherited_slots = set( 1160*cda5da8dSAndroid Build Coastguard Worker itertools.chain.from_iterable(map(_get_slots, cls.__mro__[1:-1])) 1161*cda5da8dSAndroid Build Coastguard Worker ) 1162*cda5da8dSAndroid Build Coastguard Worker # The slots for our class. Remove slots from our base classes. Add 1163*cda5da8dSAndroid Build Coastguard Worker # '__weakref__' if weakref_slot was given, unless it is already present. 1164*cda5da8dSAndroid Build Coastguard Worker cls_dict["__slots__"] = tuple( 1165*cda5da8dSAndroid Build Coastguard Worker itertools.filterfalse( 1166*cda5da8dSAndroid Build Coastguard Worker inherited_slots.__contains__, 1167*cda5da8dSAndroid Build Coastguard Worker itertools.chain( 1168*cda5da8dSAndroid Build Coastguard Worker # gh-93521: '__weakref__' also needs to be filtered out if 1169*cda5da8dSAndroid Build Coastguard Worker # already present in inherited_slots 1170*cda5da8dSAndroid Build Coastguard Worker field_names, ('__weakref__',) if weakref_slot else () 1171*cda5da8dSAndroid Build Coastguard Worker ) 1172*cda5da8dSAndroid Build Coastguard Worker ), 1173*cda5da8dSAndroid Build Coastguard Worker ) 1174*cda5da8dSAndroid Build Coastguard Worker 1175*cda5da8dSAndroid Build Coastguard Worker for field_name in field_names: 1176*cda5da8dSAndroid Build Coastguard Worker # Remove our attributes, if present. They'll still be 1177*cda5da8dSAndroid Build Coastguard Worker # available in _MARKER. 1178*cda5da8dSAndroid Build Coastguard Worker cls_dict.pop(field_name, None) 1179*cda5da8dSAndroid Build Coastguard Worker 1180*cda5da8dSAndroid Build Coastguard Worker # Remove __dict__ itself. 1181*cda5da8dSAndroid Build Coastguard Worker cls_dict.pop('__dict__', None) 1182*cda5da8dSAndroid Build Coastguard Worker 1183*cda5da8dSAndroid Build Coastguard Worker # Clear existing `__weakref__` descriptor, it belongs to a previous type: 1184*cda5da8dSAndroid Build Coastguard Worker cls_dict.pop('__weakref__', None) # gh-102069 1185*cda5da8dSAndroid Build Coastguard Worker 1186*cda5da8dSAndroid Build Coastguard Worker # And finally create the class. 1187*cda5da8dSAndroid Build Coastguard Worker qualname = getattr(cls, '__qualname__', None) 1188*cda5da8dSAndroid Build Coastguard Worker cls = type(cls)(cls.__name__, cls.__bases__, cls_dict) 1189*cda5da8dSAndroid Build Coastguard Worker if qualname is not None: 1190*cda5da8dSAndroid Build Coastguard Worker cls.__qualname__ = qualname 1191*cda5da8dSAndroid Build Coastguard Worker 1192*cda5da8dSAndroid Build Coastguard Worker if is_frozen: 1193*cda5da8dSAndroid Build Coastguard Worker # Need this for pickling frozen classes with slots. 1194*cda5da8dSAndroid Build Coastguard Worker if '__getstate__' not in cls_dict: 1195*cda5da8dSAndroid Build Coastguard Worker cls.__getstate__ = _dataclass_getstate 1196*cda5da8dSAndroid Build Coastguard Worker if '__setstate__' not in cls_dict: 1197*cda5da8dSAndroid Build Coastguard Worker cls.__setstate__ = _dataclass_setstate 1198*cda5da8dSAndroid Build Coastguard Worker 1199*cda5da8dSAndroid Build Coastguard Worker return cls 1200*cda5da8dSAndroid Build Coastguard Worker 1201*cda5da8dSAndroid Build Coastguard Worker 1202*cda5da8dSAndroid Build Coastguard Workerdef dataclass(cls=None, /, *, init=True, repr=True, eq=True, order=False, 1203*cda5da8dSAndroid Build Coastguard Worker unsafe_hash=False, frozen=False, match_args=True, 1204*cda5da8dSAndroid Build Coastguard Worker kw_only=False, slots=False, weakref_slot=False): 1205*cda5da8dSAndroid Build Coastguard Worker """Add dunder methods based on the fields defined in the class. 1206*cda5da8dSAndroid Build Coastguard Worker 1207*cda5da8dSAndroid Build Coastguard Worker Examines PEP 526 __annotations__ to determine fields. 1208*cda5da8dSAndroid Build Coastguard Worker 1209*cda5da8dSAndroid Build Coastguard Worker If init is true, an __init__() method is added to the class. If repr 1210*cda5da8dSAndroid Build Coastguard Worker is true, a __repr__() method is added. If order is true, rich 1211*cda5da8dSAndroid Build Coastguard Worker comparison dunder methods are added. If unsafe_hash is true, a 1212*cda5da8dSAndroid Build Coastguard Worker __hash__() method is added. If frozen is true, fields may not be 1213*cda5da8dSAndroid Build Coastguard Worker assigned to after instance creation. If match_args is true, the 1214*cda5da8dSAndroid Build Coastguard Worker __match_args__ tuple is added. If kw_only is true, then by default 1215*cda5da8dSAndroid Build Coastguard Worker all fields are keyword-only. If slots is true, a new class with a 1216*cda5da8dSAndroid Build Coastguard Worker __slots__ attribute is returned. 1217*cda5da8dSAndroid Build Coastguard Worker """ 1218*cda5da8dSAndroid Build Coastguard Worker 1219*cda5da8dSAndroid Build Coastguard Worker def wrap(cls): 1220*cda5da8dSAndroid Build Coastguard Worker return _process_class(cls, init, repr, eq, order, unsafe_hash, 1221*cda5da8dSAndroid Build Coastguard Worker frozen, match_args, kw_only, slots, 1222*cda5da8dSAndroid Build Coastguard Worker weakref_slot) 1223*cda5da8dSAndroid Build Coastguard Worker 1224*cda5da8dSAndroid Build Coastguard Worker # See if we're being called as @dataclass or @dataclass(). 1225*cda5da8dSAndroid Build Coastguard Worker if cls is None: 1226*cda5da8dSAndroid Build Coastguard Worker # We're called with parens. 1227*cda5da8dSAndroid Build Coastguard Worker return wrap 1228*cda5da8dSAndroid Build Coastguard Worker 1229*cda5da8dSAndroid Build Coastguard Worker # We're called as @dataclass without parens. 1230*cda5da8dSAndroid Build Coastguard Worker return wrap(cls) 1231*cda5da8dSAndroid Build Coastguard Worker 1232*cda5da8dSAndroid Build Coastguard Worker 1233*cda5da8dSAndroid Build Coastguard Workerdef fields(class_or_instance): 1234*cda5da8dSAndroid Build Coastguard Worker """Return a tuple describing the fields of this dataclass. 1235*cda5da8dSAndroid Build Coastguard Worker 1236*cda5da8dSAndroid Build Coastguard Worker Accepts a dataclass or an instance of one. Tuple elements are of 1237*cda5da8dSAndroid Build Coastguard Worker type Field. 1238*cda5da8dSAndroid Build Coastguard Worker """ 1239*cda5da8dSAndroid Build Coastguard Worker 1240*cda5da8dSAndroid Build Coastguard Worker # Might it be worth caching this, per class? 1241*cda5da8dSAndroid Build Coastguard Worker try: 1242*cda5da8dSAndroid Build Coastguard Worker fields = getattr(class_or_instance, _FIELDS) 1243*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 1244*cda5da8dSAndroid Build Coastguard Worker raise TypeError('must be called with a dataclass type or instance') from None 1245*cda5da8dSAndroid Build Coastguard Worker 1246*cda5da8dSAndroid Build Coastguard Worker # Exclude pseudo-fields. Note that fields is sorted by insertion 1247*cda5da8dSAndroid Build Coastguard Worker # order, so the order of the tuple is as the fields were defined. 1248*cda5da8dSAndroid Build Coastguard Worker return tuple(f for f in fields.values() if f._field_type is _FIELD) 1249*cda5da8dSAndroid Build Coastguard Worker 1250*cda5da8dSAndroid Build Coastguard Worker 1251*cda5da8dSAndroid Build Coastguard Workerdef _is_dataclass_instance(obj): 1252*cda5da8dSAndroid Build Coastguard Worker """Returns True if obj is an instance of a dataclass.""" 1253*cda5da8dSAndroid Build Coastguard Worker return hasattr(type(obj), _FIELDS) 1254*cda5da8dSAndroid Build Coastguard Worker 1255*cda5da8dSAndroid Build Coastguard Worker 1256*cda5da8dSAndroid Build Coastguard Workerdef is_dataclass(obj): 1257*cda5da8dSAndroid Build Coastguard Worker """Returns True if obj is a dataclass or an instance of a 1258*cda5da8dSAndroid Build Coastguard Worker dataclass.""" 1259*cda5da8dSAndroid Build Coastguard Worker cls = obj if isinstance(obj, type) else type(obj) 1260*cda5da8dSAndroid Build Coastguard Worker return hasattr(cls, _FIELDS) 1261*cda5da8dSAndroid Build Coastguard Worker 1262*cda5da8dSAndroid Build Coastguard Worker 1263*cda5da8dSAndroid Build Coastguard Workerdef asdict(obj, *, dict_factory=dict): 1264*cda5da8dSAndroid Build Coastguard Worker """Return the fields of a dataclass instance as a new dictionary mapping 1265*cda5da8dSAndroid Build Coastguard Worker field names to field values. 1266*cda5da8dSAndroid Build Coastguard Worker 1267*cda5da8dSAndroid Build Coastguard Worker Example usage:: 1268*cda5da8dSAndroid Build Coastguard Worker 1269*cda5da8dSAndroid Build Coastguard Worker @dataclass 1270*cda5da8dSAndroid Build Coastguard Worker class C: 1271*cda5da8dSAndroid Build Coastguard Worker x: int 1272*cda5da8dSAndroid Build Coastguard Worker y: int 1273*cda5da8dSAndroid Build Coastguard Worker 1274*cda5da8dSAndroid Build Coastguard Worker c = C(1, 2) 1275*cda5da8dSAndroid Build Coastguard Worker assert asdict(c) == {'x': 1, 'y': 2} 1276*cda5da8dSAndroid Build Coastguard Worker 1277*cda5da8dSAndroid Build Coastguard Worker If given, 'dict_factory' will be used instead of built-in dict. 1278*cda5da8dSAndroid Build Coastguard Worker The function applies recursively to field values that are 1279*cda5da8dSAndroid Build Coastguard Worker dataclass instances. This will also look into built-in containers: 1280*cda5da8dSAndroid Build Coastguard Worker tuples, lists, and dicts. 1281*cda5da8dSAndroid Build Coastguard Worker """ 1282*cda5da8dSAndroid Build Coastguard Worker if not _is_dataclass_instance(obj): 1283*cda5da8dSAndroid Build Coastguard Worker raise TypeError("asdict() should be called on dataclass instances") 1284*cda5da8dSAndroid Build Coastguard Worker return _asdict_inner(obj, dict_factory) 1285*cda5da8dSAndroid Build Coastguard Worker 1286*cda5da8dSAndroid Build Coastguard Worker 1287*cda5da8dSAndroid Build Coastguard Workerdef _asdict_inner(obj, dict_factory): 1288*cda5da8dSAndroid Build Coastguard Worker if _is_dataclass_instance(obj): 1289*cda5da8dSAndroid Build Coastguard Worker result = [] 1290*cda5da8dSAndroid Build Coastguard Worker for f in fields(obj): 1291*cda5da8dSAndroid Build Coastguard Worker value = _asdict_inner(getattr(obj, f.name), dict_factory) 1292*cda5da8dSAndroid Build Coastguard Worker result.append((f.name, value)) 1293*cda5da8dSAndroid Build Coastguard Worker return dict_factory(result) 1294*cda5da8dSAndroid Build Coastguard Worker elif isinstance(obj, tuple) and hasattr(obj, '_fields'): 1295*cda5da8dSAndroid Build Coastguard Worker # obj is a namedtuple. Recurse into it, but the returned 1296*cda5da8dSAndroid Build Coastguard Worker # object is another namedtuple of the same type. This is 1297*cda5da8dSAndroid Build Coastguard Worker # similar to how other list- or tuple-derived classes are 1298*cda5da8dSAndroid Build Coastguard Worker # treated (see below), but we just need to create them 1299*cda5da8dSAndroid Build Coastguard Worker # differently because a namedtuple's __init__ needs to be 1300*cda5da8dSAndroid Build Coastguard Worker # called differently (see bpo-34363). 1301*cda5da8dSAndroid Build Coastguard Worker 1302*cda5da8dSAndroid Build Coastguard Worker # I'm not using namedtuple's _asdict() 1303*cda5da8dSAndroid Build Coastguard Worker # method, because: 1304*cda5da8dSAndroid Build Coastguard Worker # - it does not recurse in to the namedtuple fields and 1305*cda5da8dSAndroid Build Coastguard Worker # convert them to dicts (using dict_factory). 1306*cda5da8dSAndroid Build Coastguard Worker # - I don't actually want to return a dict here. The main 1307*cda5da8dSAndroid Build Coastguard Worker # use case here is json.dumps, and it handles converting 1308*cda5da8dSAndroid Build Coastguard Worker # namedtuples to lists. Admittedly we're losing some 1309*cda5da8dSAndroid Build Coastguard Worker # information here when we produce a json list instead of a 1310*cda5da8dSAndroid Build Coastguard Worker # dict. Note that if we returned dicts here instead of 1311*cda5da8dSAndroid Build Coastguard Worker # namedtuples, we could no longer call asdict() on a data 1312*cda5da8dSAndroid Build Coastguard Worker # structure where a namedtuple was used as a dict key. 1313*cda5da8dSAndroid Build Coastguard Worker 1314*cda5da8dSAndroid Build Coastguard Worker return type(obj)(*[_asdict_inner(v, dict_factory) for v in obj]) 1315*cda5da8dSAndroid Build Coastguard Worker elif isinstance(obj, (list, tuple)): 1316*cda5da8dSAndroid Build Coastguard Worker # Assume we can create an object of this type by passing in a 1317*cda5da8dSAndroid Build Coastguard Worker # generator (which is not true for namedtuples, handled 1318*cda5da8dSAndroid Build Coastguard Worker # above). 1319*cda5da8dSAndroid Build Coastguard Worker return type(obj)(_asdict_inner(v, dict_factory) for v in obj) 1320*cda5da8dSAndroid Build Coastguard Worker elif isinstance(obj, dict): 1321*cda5da8dSAndroid Build Coastguard Worker return type(obj)((_asdict_inner(k, dict_factory), 1322*cda5da8dSAndroid Build Coastguard Worker _asdict_inner(v, dict_factory)) 1323*cda5da8dSAndroid Build Coastguard Worker for k, v in obj.items()) 1324*cda5da8dSAndroid Build Coastguard Worker else: 1325*cda5da8dSAndroid Build Coastguard Worker return copy.deepcopy(obj) 1326*cda5da8dSAndroid Build Coastguard Worker 1327*cda5da8dSAndroid Build Coastguard Worker 1328*cda5da8dSAndroid Build Coastguard Workerdef astuple(obj, *, tuple_factory=tuple): 1329*cda5da8dSAndroid Build Coastguard Worker """Return the fields of a dataclass instance as a new tuple of field values. 1330*cda5da8dSAndroid Build Coastguard Worker 1331*cda5da8dSAndroid Build Coastguard Worker Example usage:: 1332*cda5da8dSAndroid Build Coastguard Worker 1333*cda5da8dSAndroid Build Coastguard Worker @dataclass 1334*cda5da8dSAndroid Build Coastguard Worker class C: 1335*cda5da8dSAndroid Build Coastguard Worker x: int 1336*cda5da8dSAndroid Build Coastguard Worker y: int 1337*cda5da8dSAndroid Build Coastguard Worker 1338*cda5da8dSAndroid Build Coastguard Worker c = C(1, 2) 1339*cda5da8dSAndroid Build Coastguard Worker assert astuple(c) == (1, 2) 1340*cda5da8dSAndroid Build Coastguard Worker 1341*cda5da8dSAndroid Build Coastguard Worker If given, 'tuple_factory' will be used instead of built-in tuple. 1342*cda5da8dSAndroid Build Coastguard Worker The function applies recursively to field values that are 1343*cda5da8dSAndroid Build Coastguard Worker dataclass instances. This will also look into built-in containers: 1344*cda5da8dSAndroid Build Coastguard Worker tuples, lists, and dicts. 1345*cda5da8dSAndroid Build Coastguard Worker """ 1346*cda5da8dSAndroid Build Coastguard Worker 1347*cda5da8dSAndroid Build Coastguard Worker if not _is_dataclass_instance(obj): 1348*cda5da8dSAndroid Build Coastguard Worker raise TypeError("astuple() should be called on dataclass instances") 1349*cda5da8dSAndroid Build Coastguard Worker return _astuple_inner(obj, tuple_factory) 1350*cda5da8dSAndroid Build Coastguard Worker 1351*cda5da8dSAndroid Build Coastguard Worker 1352*cda5da8dSAndroid Build Coastguard Workerdef _astuple_inner(obj, tuple_factory): 1353*cda5da8dSAndroid Build Coastguard Worker if _is_dataclass_instance(obj): 1354*cda5da8dSAndroid Build Coastguard Worker result = [] 1355*cda5da8dSAndroid Build Coastguard Worker for f in fields(obj): 1356*cda5da8dSAndroid Build Coastguard Worker value = _astuple_inner(getattr(obj, f.name), tuple_factory) 1357*cda5da8dSAndroid Build Coastguard Worker result.append(value) 1358*cda5da8dSAndroid Build Coastguard Worker return tuple_factory(result) 1359*cda5da8dSAndroid Build Coastguard Worker elif isinstance(obj, tuple) and hasattr(obj, '_fields'): 1360*cda5da8dSAndroid Build Coastguard Worker # obj is a namedtuple. Recurse into it, but the returned 1361*cda5da8dSAndroid Build Coastguard Worker # object is another namedtuple of the same type. This is 1362*cda5da8dSAndroid Build Coastguard Worker # similar to how other list- or tuple-derived classes are 1363*cda5da8dSAndroid Build Coastguard Worker # treated (see below), but we just need to create them 1364*cda5da8dSAndroid Build Coastguard Worker # differently because a namedtuple's __init__ needs to be 1365*cda5da8dSAndroid Build Coastguard Worker # called differently (see bpo-34363). 1366*cda5da8dSAndroid Build Coastguard Worker return type(obj)(*[_astuple_inner(v, tuple_factory) for v in obj]) 1367*cda5da8dSAndroid Build Coastguard Worker elif isinstance(obj, (list, tuple)): 1368*cda5da8dSAndroid Build Coastguard Worker # Assume we can create an object of this type by passing in a 1369*cda5da8dSAndroid Build Coastguard Worker # generator (which is not true for namedtuples, handled 1370*cda5da8dSAndroid Build Coastguard Worker # above). 1371*cda5da8dSAndroid Build Coastguard Worker return type(obj)(_astuple_inner(v, tuple_factory) for v in obj) 1372*cda5da8dSAndroid Build Coastguard Worker elif isinstance(obj, dict): 1373*cda5da8dSAndroid Build Coastguard Worker return type(obj)((_astuple_inner(k, tuple_factory), _astuple_inner(v, tuple_factory)) 1374*cda5da8dSAndroid Build Coastguard Worker for k, v in obj.items()) 1375*cda5da8dSAndroid Build Coastguard Worker else: 1376*cda5da8dSAndroid Build Coastguard Worker return copy.deepcopy(obj) 1377*cda5da8dSAndroid Build Coastguard Worker 1378*cda5da8dSAndroid Build Coastguard Worker 1379*cda5da8dSAndroid Build Coastguard Workerdef make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, 1380*cda5da8dSAndroid Build Coastguard Worker repr=True, eq=True, order=False, unsafe_hash=False, 1381*cda5da8dSAndroid Build Coastguard Worker frozen=False, match_args=True, kw_only=False, slots=False, 1382*cda5da8dSAndroid Build Coastguard Worker weakref_slot=False): 1383*cda5da8dSAndroid Build Coastguard Worker """Return a new dynamically created dataclass. 1384*cda5da8dSAndroid Build Coastguard Worker 1385*cda5da8dSAndroid Build Coastguard Worker The dataclass name will be 'cls_name'. 'fields' is an iterable 1386*cda5da8dSAndroid Build Coastguard Worker of either (name), (name, type) or (name, type, Field) objects. If type is 1387*cda5da8dSAndroid Build Coastguard Worker omitted, use the string 'typing.Any'. Field objects are created by 1388*cda5da8dSAndroid Build Coastguard Worker the equivalent of calling 'field(name, type [, Field-info])'.:: 1389*cda5da8dSAndroid Build Coastguard Worker 1390*cda5da8dSAndroid Build Coastguard Worker C = make_dataclass('C', ['x', ('y', int), ('z', int, field(init=False))], bases=(Base,)) 1391*cda5da8dSAndroid Build Coastguard Worker 1392*cda5da8dSAndroid Build Coastguard Worker is equivalent to:: 1393*cda5da8dSAndroid Build Coastguard Worker 1394*cda5da8dSAndroid Build Coastguard Worker @dataclass 1395*cda5da8dSAndroid Build Coastguard Worker class C(Base): 1396*cda5da8dSAndroid Build Coastguard Worker x: 'typing.Any' 1397*cda5da8dSAndroid Build Coastguard Worker y: int 1398*cda5da8dSAndroid Build Coastguard Worker z: int = field(init=False) 1399*cda5da8dSAndroid Build Coastguard Worker 1400*cda5da8dSAndroid Build Coastguard Worker For the bases and namespace parameters, see the builtin type() function. 1401*cda5da8dSAndroid Build Coastguard Worker 1402*cda5da8dSAndroid Build Coastguard Worker The parameters init, repr, eq, order, unsafe_hash, and frozen are passed to 1403*cda5da8dSAndroid Build Coastguard Worker dataclass(). 1404*cda5da8dSAndroid Build Coastguard Worker """ 1405*cda5da8dSAndroid Build Coastguard Worker 1406*cda5da8dSAndroid Build Coastguard Worker if namespace is None: 1407*cda5da8dSAndroid Build Coastguard Worker namespace = {} 1408*cda5da8dSAndroid Build Coastguard Worker 1409*cda5da8dSAndroid Build Coastguard Worker # While we're looking through the field names, validate that they 1410*cda5da8dSAndroid Build Coastguard Worker # are identifiers, are not keywords, and not duplicates. 1411*cda5da8dSAndroid Build Coastguard Worker seen = set() 1412*cda5da8dSAndroid Build Coastguard Worker annotations = {} 1413*cda5da8dSAndroid Build Coastguard Worker defaults = {} 1414*cda5da8dSAndroid Build Coastguard Worker for item in fields: 1415*cda5da8dSAndroid Build Coastguard Worker if isinstance(item, str): 1416*cda5da8dSAndroid Build Coastguard Worker name = item 1417*cda5da8dSAndroid Build Coastguard Worker tp = 'typing.Any' 1418*cda5da8dSAndroid Build Coastguard Worker elif len(item) == 2: 1419*cda5da8dSAndroid Build Coastguard Worker name, tp, = item 1420*cda5da8dSAndroid Build Coastguard Worker elif len(item) == 3: 1421*cda5da8dSAndroid Build Coastguard Worker name, tp, spec = item 1422*cda5da8dSAndroid Build Coastguard Worker defaults[name] = spec 1423*cda5da8dSAndroid Build Coastguard Worker else: 1424*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f'Invalid field: {item!r}') 1425*cda5da8dSAndroid Build Coastguard Worker 1426*cda5da8dSAndroid Build Coastguard Worker if not isinstance(name, str) or not name.isidentifier(): 1427*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f'Field names must be valid identifiers: {name!r}') 1428*cda5da8dSAndroid Build Coastguard Worker if keyword.iskeyword(name): 1429*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f'Field names must not be keywords: {name!r}') 1430*cda5da8dSAndroid Build Coastguard Worker if name in seen: 1431*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f'Field name duplicated: {name!r}') 1432*cda5da8dSAndroid Build Coastguard Worker 1433*cda5da8dSAndroid Build Coastguard Worker seen.add(name) 1434*cda5da8dSAndroid Build Coastguard Worker annotations[name] = tp 1435*cda5da8dSAndroid Build Coastguard Worker 1436*cda5da8dSAndroid Build Coastguard Worker # Update 'ns' with the user-supplied namespace plus our calculated values. 1437*cda5da8dSAndroid Build Coastguard Worker def exec_body_callback(ns): 1438*cda5da8dSAndroid Build Coastguard Worker ns.update(namespace) 1439*cda5da8dSAndroid Build Coastguard Worker ns.update(defaults) 1440*cda5da8dSAndroid Build Coastguard Worker ns['__annotations__'] = annotations 1441*cda5da8dSAndroid Build Coastguard Worker 1442*cda5da8dSAndroid Build Coastguard Worker # We use `types.new_class()` instead of simply `type()` to allow dynamic creation 1443*cda5da8dSAndroid Build Coastguard Worker # of generic dataclasses. 1444*cda5da8dSAndroid Build Coastguard Worker cls = types.new_class(cls_name, bases, {}, exec_body_callback) 1445*cda5da8dSAndroid Build Coastguard Worker 1446*cda5da8dSAndroid Build Coastguard Worker # Apply the normal decorator. 1447*cda5da8dSAndroid Build Coastguard Worker return dataclass(cls, init=init, repr=repr, eq=eq, order=order, 1448*cda5da8dSAndroid Build Coastguard Worker unsafe_hash=unsafe_hash, frozen=frozen, 1449*cda5da8dSAndroid Build Coastguard Worker match_args=match_args, kw_only=kw_only, slots=slots, 1450*cda5da8dSAndroid Build Coastguard Worker weakref_slot=weakref_slot) 1451*cda5da8dSAndroid Build Coastguard Worker 1452*cda5da8dSAndroid Build Coastguard Worker 1453*cda5da8dSAndroid Build Coastguard Workerdef replace(obj, /, **changes): 1454*cda5da8dSAndroid Build Coastguard Worker """Return a new object replacing specified fields with new values. 1455*cda5da8dSAndroid Build Coastguard Worker 1456*cda5da8dSAndroid Build Coastguard Worker This is especially useful for frozen classes. Example usage:: 1457*cda5da8dSAndroid Build Coastguard Worker 1458*cda5da8dSAndroid Build Coastguard Worker @dataclass(frozen=True) 1459*cda5da8dSAndroid Build Coastguard Worker class C: 1460*cda5da8dSAndroid Build Coastguard Worker x: int 1461*cda5da8dSAndroid Build Coastguard Worker y: int 1462*cda5da8dSAndroid Build Coastguard Worker 1463*cda5da8dSAndroid Build Coastguard Worker c = C(1, 2) 1464*cda5da8dSAndroid Build Coastguard Worker c1 = replace(c, x=3) 1465*cda5da8dSAndroid Build Coastguard Worker assert c1.x == 3 and c1.y == 2 1466*cda5da8dSAndroid Build Coastguard Worker """ 1467*cda5da8dSAndroid Build Coastguard Worker 1468*cda5da8dSAndroid Build Coastguard Worker # We're going to mutate 'changes', but that's okay because it's a 1469*cda5da8dSAndroid Build Coastguard Worker # new dict, even if called with 'replace(obj, **my_changes)'. 1470*cda5da8dSAndroid Build Coastguard Worker 1471*cda5da8dSAndroid Build Coastguard Worker if not _is_dataclass_instance(obj): 1472*cda5da8dSAndroid Build Coastguard Worker raise TypeError("replace() should be called on dataclass instances") 1473*cda5da8dSAndroid Build Coastguard Worker 1474*cda5da8dSAndroid Build Coastguard Worker # It's an error to have init=False fields in 'changes'. 1475*cda5da8dSAndroid Build Coastguard Worker # If a field is not in 'changes', read its value from the provided obj. 1476*cda5da8dSAndroid Build Coastguard Worker 1477*cda5da8dSAndroid Build Coastguard Worker for f in getattr(obj, _FIELDS).values(): 1478*cda5da8dSAndroid Build Coastguard Worker # Only consider normal fields or InitVars. 1479*cda5da8dSAndroid Build Coastguard Worker if f._field_type is _FIELD_CLASSVAR: 1480*cda5da8dSAndroid Build Coastguard Worker continue 1481*cda5da8dSAndroid Build Coastguard Worker 1482*cda5da8dSAndroid Build Coastguard Worker if not f.init: 1483*cda5da8dSAndroid Build Coastguard Worker # Error if this field is specified in changes. 1484*cda5da8dSAndroid Build Coastguard Worker if f.name in changes: 1485*cda5da8dSAndroid Build Coastguard Worker raise ValueError(f'field {f.name} is declared with ' 1486*cda5da8dSAndroid Build Coastguard Worker 'init=False, it cannot be specified with ' 1487*cda5da8dSAndroid Build Coastguard Worker 'replace()') 1488*cda5da8dSAndroid Build Coastguard Worker continue 1489*cda5da8dSAndroid Build Coastguard Worker 1490*cda5da8dSAndroid Build Coastguard Worker if f.name not in changes: 1491*cda5da8dSAndroid Build Coastguard Worker if f._field_type is _FIELD_INITVAR and f.default is MISSING: 1492*cda5da8dSAndroid Build Coastguard Worker raise ValueError(f"InitVar {f.name!r} " 1493*cda5da8dSAndroid Build Coastguard Worker 'must be specified with replace()') 1494*cda5da8dSAndroid Build Coastguard Worker changes[f.name] = getattr(obj, f.name) 1495*cda5da8dSAndroid Build Coastguard Worker 1496*cda5da8dSAndroid Build Coastguard Worker # Create the new object, which calls __init__() and 1497*cda5da8dSAndroid Build Coastguard Worker # __post_init__() (if defined), using all of the init fields we've 1498*cda5da8dSAndroid Build Coastguard Worker # added and/or left in 'changes'. If there are values supplied in 1499*cda5da8dSAndroid Build Coastguard Worker # changes that aren't fields, this will correctly raise a 1500*cda5da8dSAndroid Build Coastguard Worker # TypeError. 1501*cda5da8dSAndroid Build Coastguard Worker return obj.__class__(**changes) 1502