Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
dataclasses.py1805 linesDownload Raw Back to Lib
1import re2import sys3import copy4import types5import inspect6import keyword7import itertools8import annotationlib9import abc10from reprlib import recursive_repr11 12 13__all__ = ['dataclass',14           'field',15           'Field',16           'FrozenInstanceError',17           'InitVar',18           'KW_ONLY',19           'MISSING',20 21           # Helper functions.22           'fields',23           'asdict',24           'astuple',25           'make_dataclass',26           'replace',27           'is_dataclass',28           ]29 30# Conditions for adding methods.  The boxes indicate what action the31# dataclass decorator takes.  For all of these tables, when I talk32# about init=, repr=, eq=, order=, unsafe_hash=, or frozen=, I'm33# referring to the arguments to the @dataclass decorator.  When34# checking if a dunder method already exists, I mean check for an35# entry in the class's __dict__.  I never check to see if an attribute36# is defined in a base class.37 38# Key:39# +=========+=========================================+40# + Value   | Meaning                                 |41# +=========+=========================================+42# | <blank> | No action: no method is added.          |43# +---------+-----------------------------------------+44# | add     | Generated method is added.              |45# +---------+-----------------------------------------+46# | raise   | TypeError is raised.                    |47# +---------+-----------------------------------------+48# | None    | Attribute is set to None.               |49# +=========+=========================================+50 51# __init__52#53#   +--- init= parameter54#   |55#   v     |       |       |56#         |  no   |  yes  |  <--- class has __init__ in __dict__?57# +=======+=======+=======+58# | False |       |       |59# +-------+-------+-------+60# | True  | add   |       |  <- the default61# +=======+=======+=======+62 63# __repr__64#65#    +--- repr= parameter66#    |67#    v    |       |       |68#         |  no   |  yes  |  <--- class has __repr__ in __dict__?69# +=======+=======+=======+70# | False |       |       |71# +-------+-------+-------+72# | True  | add   |       |  <- the default73# +=======+=======+=======+74 75 76# __setattr__77# __delattr__78#79#    +--- frozen= parameter80#    |81#    v    |       |       |82#         |  no   |  yes  |  <--- class has __setattr__ or __delattr__ in __dict__?83# +=======+=======+=======+84# | False |       |       |  <- the default85# +-------+-------+-------+86# | True  | add   | raise |87# +=======+=======+=======+88# Raise because not adding these methods would break the "frozen-ness"89# of the class.90 91# __eq__92#93#    +--- eq= parameter94#    |95#    v    |       |       |96#         |  no   |  yes  |  <--- class has __eq__ in __dict__?97# +=======+=======+=======+98# | False |       |       |99# +-------+-------+-------+100# | True  | add   |       |  <- the default101# +=======+=======+=======+102 103# __lt__104# __le__105# __gt__106# __ge__107#108#    +--- order= parameter109#    |110#    v    |       |       |111#         |  no   |  yes  |  <--- class has any comparison method in __dict__?112# +=======+=======+=======+113# | False |       |       |  <- the default114# +-------+-------+-------+115# | True  | add   | raise |116# +=======+=======+=======+117# Raise because to allow this case would interfere with using118# functools.total_ordering.119 120# __hash__121 122#    +------------------- unsafe_hash= parameter123#    |       +----------- eq= parameter124#    |       |       +--- frozen= parameter125#    |       |       |126#    v       v       v    |        |        |127#                         |   no   |  yes   |  <--- class has explicitly defined __hash__128# +=======+=======+=======+========+========+129# | False | False | False |        |        | No __eq__, use the base class __hash__130# +-------+-------+-------+--------+--------+131# | False | False | True  |        |        | No __eq__, use the base class __hash__132# +-------+-------+-------+--------+--------+133# | False | True  | False | None   |        | <-- the default, not hashable134# +-------+-------+-------+--------+--------+135# | False | True  | True  | add    |        | Frozen, so hashable, allows override136# +-------+-------+-------+--------+--------+137# | True  | False | False | add    | raise  | Has no __eq__, but hashable138# +-------+-------+-------+--------+--------+139# | True  | False | True  | add    | raise  | Has no __eq__, but hashable140# +-------+-------+-------+--------+--------+141# | True  | True  | False | add    | raise  | Not frozen, but hashable142# +-------+-------+-------+--------+--------+143# | True  | True  | True  | add    | raise  | Frozen, so hashable144# +=======+=======+=======+========+========+145# For boxes that are blank, __hash__ is untouched and therefore146# inherited from the base class.  If the base is object, then147# id-based hashing is used.148#149# Note that a class may already have __hash__=None if it specified an150# __eq__ method in the class body (not one that was created by151# @dataclass).152#153# See _hash_action (below) for a coded version of this table.154 155# __match_args__156#157#    +--- match_args= parameter158#    |159#    v    |       |       |160#         |  no   |  yes  |  <--- class has __match_args__ in __dict__?161# +=======+=======+=======+162# | False |       |       |163# +-------+-------+-------+164# | True  | add   |       |  <- the default165# +=======+=======+=======+166# __match_args__ is always added unless the class already defines it. It is a167# tuple of __init__ parameter names; non-init fields must be matched by keyword.168 169 170# Raised when an attempt is made to modify a frozen class.171class FrozenInstanceError(AttributeError): pass172 173# A sentinel object for default values to signal that a default174# factory will be used.  This is given a nice repr() which will appear175# in the function signature of dataclasses' constructors.176class _HAS_DEFAULT_FACTORY_CLASS:177    def __repr__(self):178        return '<factory>'179_HAS_DEFAULT_FACTORY = _HAS_DEFAULT_FACTORY_CLASS()180 181# A sentinel object to detect if a parameter is supplied or not.  Use182# a class to give it a better repr.183class _MISSING_TYPE:184    pass185MISSING = _MISSING_TYPE()186 187# A sentinel object to indicate that following fields are keyword-only by188# default.  Use a class to give it a better repr.189class _KW_ONLY_TYPE:190    pass191KW_ONLY = _KW_ONLY_TYPE()192 193# Since most per-field metadata will be unused, create an empty194# read-only proxy that can be shared among all fields.195_EMPTY_METADATA = types.MappingProxyType({})196 197# Markers for the various kinds of fields and pseudo-fields.198class _FIELD_BASE:199    def __init__(self, name):200        self.name = name201    def __repr__(self):202        return self.name203_FIELD = _FIELD_BASE('_FIELD')204_FIELD_CLASSVAR = _FIELD_BASE('_FIELD_CLASSVAR')205_FIELD_INITVAR = _FIELD_BASE('_FIELD_INITVAR')206 207# The name of an attribute on the class where we store the Field208# objects.  Also used to check if a class is a Data Class.209_FIELDS = '__dataclass_fields__'210 211# The name of an attribute on the class that stores the parameters to212# @dataclass.213_PARAMS = '__dataclass_params__'214 215# The name of the function, that if it exists, is called at the end of216# __init__.217_POST_INIT_NAME = '__post_init__'218 219# String regex that string annotations for ClassVar or InitVar must match.220# Allows "identifier.identifier[" or "identifier[".221# https://bugs.python.org/issue33453 for details.222_MODULE_IDENTIFIER_RE = re.compile(r'^(?:\s*(\w+)\s*\.)?\s*(\w+)')223 224# Atomic immutable types which don't require any recursive handling and for which deepcopy225# returns the same object. We can provide a fast-path for these types in asdict and astuple.226_ATOMIC_TYPES = frozenset({227    # Common JSON Serializable types228    types.NoneType,229    bool,230    int,231    float,232    str,233    # Other common types234    complex,235    bytes,236    # Other types that are also unaffected by deepcopy237    types.EllipsisType,238    types.NotImplementedType,239    types.CodeType,240    types.BuiltinFunctionType,241    types.FunctionType,242    type,243    range,244    property,245})246 247# Any marker is used in `make_dataclass` to mark unannotated fields as `Any`248# without importing `typing` module.249_ANY_MARKER = object()250 251 252class InitVar:253    __slots__ = ('type', )254 255    def __init__(self, type):256        self.type = type257 258    def __repr__(self):259        if isinstance(self.type, type):260            type_name = self.type.__name__261        else:262            # typing objects, e.g. List[int]263            type_name = repr(self.type)264        return f'dataclasses.InitVar[{type_name}]'265 266    def __class_getitem__(cls, type):267        return InitVar(type)268 269# Instances of Field are only ever created from within this module,270# and only from the field() function, although Field instances are271# exposed externally as (conceptually) read-only objects.272#273# name and type are filled in after the fact, not in __init__.274# They're not known at the time this class is instantiated, but it's275# convenient if they're available later.276#277# When cls._FIELDS is filled in with a list of Field objects, the name278# and type fields will have been populated.279class Field:280    __slots__ = ('name',281                 'type',282                 'default',283                 'default_factory',284                 'repr',285                 'hash',286                 'init',287                 'compare',288                 'metadata',289                 'kw_only',290                 'doc',291                 '_field_type',  # Private: not to be used by user code.292                 )293 294    def __init__(self, default, default_factory, init, repr, hash, compare,295                 metadata, kw_only, doc):296        self.name = None297        self.type = None298        self.default = default299        self.default_factory = default_factory300        self.init = init301        self.repr = repr302        self.hash = hash303        self.compare = compare304        self.metadata = (_EMPTY_METADATA305                         if metadata is None else306                         types.MappingProxyType(metadata))307        self.kw_only = kw_only308        self.doc = doc309        self._field_type = None310 311    @recursive_repr()312    def __repr__(self):313        return ('Field('314                f'name={self.name!r},'315                f'type={self.type!r},'316                f'default={self.default!r},'317                f'default_factory={self.default_factory!r},'318                f'init={self.init!r},'319                f'repr={self.repr!r},'320                f'hash={self.hash!r},'321                f'compare={self.compare!r},'322                f'metadata={self.metadata!r},'323                f'kw_only={self.kw_only!r},'324                f'doc={self.doc!r},'325                f'_field_type={self._field_type}'326                ')')327 328    # This is used to support the PEP 487 __set_name__ protocol in the329    # case where we're using a field that contains a descriptor as a330    # default value.  For details on __set_name__, see331    # https://peps.python.org/pep-0487/#implementation-details.332    #333    # Note that in _process_class, this Field object is overwritten334    # with the default value, so the end result is a descriptor that335    # had __set_name__ called on it at the right time.336    def __set_name__(self, owner, name):337        func = getattr(type(self.default), '__set_name__', None)338        if func:339            # There is a __set_name__ method on the descriptor, call340            # it.341            func(self.default, owner, name)342 343    __class_getitem__ = classmethod(types.GenericAlias)344 345 346class _DataclassParams:347    __slots__ = ('init',348                 'repr',349                 'eq',350                 'order',351                 'unsafe_hash',352                 'frozen',353                 'match_args',354                 'kw_only',355                 'slots',356                 'weakref_slot',357                 )358 359    def __init__(self,360                 init, repr, eq, order, unsafe_hash, frozen,361                 match_args, kw_only, slots, weakref_slot):362        self.init = init363        self.repr = repr364        self.eq = eq365        self.order = order366        self.unsafe_hash = unsafe_hash367        self.frozen = frozen368        self.match_args = match_args369        self.kw_only = kw_only370        self.slots = slots371        self.weakref_slot = weakref_slot372 373    def __repr__(self):374        return ('_DataclassParams('375                f'init={self.init!r},'376                f'repr={self.repr!r},'377                f'eq={self.eq!r},'378                f'order={self.order!r},'379                f'unsafe_hash={self.unsafe_hash!r},'380                f'frozen={self.frozen!r},'381                f'match_args={self.match_args!r},'382                f'kw_only={self.kw_only!r},'383                f'slots={self.slots!r},'384                f'weakref_slot={self.weakref_slot!r}'385                ')')386 387 388# This function is used instead of exposing Field creation directly,389# so that a type checker can be told (via overloads) that this is a390# function whose type depends on its parameters.391def field(*, default=MISSING, default_factory=MISSING, init=True, repr=True,392          hash=None, compare=True, metadata=None, kw_only=MISSING, doc=None):393    """Return an object to identify dataclass fields.394 395    default is the default value of the field.  default_factory is a396    0-argument function called to initialize a field's value.  If init397    is true, the field will be a parameter to the class's __init__()398    function.  If repr is true, the field will be included in the399    object's repr().  If hash is true, the field will be included in the400    object's hash().  If compare is true, the field will be used in401    comparison functions.  metadata, if specified, must be a mapping402    which is stored but not otherwise examined by dataclass.  If kw_only403    is true, the field will become a keyword-only parameter to404    __init__().  doc is an optional docstring for this field.405 406    It is an error to specify both default and default_factory.407    """408 409    if default is not MISSING and default_factory is not MISSING:410        raise ValueError('cannot specify both default and default_factory')411    return Field(default, default_factory, init, repr, hash, compare,412                 metadata, kw_only, doc)413 414 415def _fields_in_init_order(fields):416    # Returns the fields as __init__ will output them.  It returns 2 tuples:417    # the first for normal args, and the second for keyword args.418 419    return (tuple(f for f in fields if f.init and not f.kw_only),420            tuple(f for f in fields if f.init and f.kw_only)421            )422 423 424def _tuple_str(obj_name, fields):425    # Return a string representing each field of obj_name as a tuple426    # member.  So, if fields is ['x', 'y'] and obj_name is "self",427    # return "(self.x,self.y)".428 429    # Special case for the 0-tuple.430    if not fields:431        return '()'432    # Note the trailing comma, needed if this turns out to be a 1-tuple.433    return f'({",".join([f"{obj_name}.{f.name}" for f in fields])},)'434 435 436class _FuncBuilder:437    def __init__(self, globals):438        self.names = []439        self.src = []440        self.globals = globals441        self.locals = {}442        self.overwrite_errors = {}443        self.unconditional_adds = {}444        self.method_annotations = {}445 446    def add_fn(self, name, args, body, *, locals=None, return_type=MISSING,447               overwrite_error=False, unconditional_add=False, decorator=None,448               annotation_fields=None):449        if locals is not None:450            self.locals.update(locals)451 452        # Keep track if this method is allowed to be overwritten if it already453        # exists in the class.  The error is method-specific, so keep it with454        # the name.  We'll use this when we generate all of the functions in455        # the add_fns_to_class call.  overwrite_error is either True, in which456        # case we'll raise an error, or it's a string, in which case we'll457        # raise an error and append this string.458        if overwrite_error:459            self.overwrite_errors[name] = overwrite_error460 461        # Should this function always overwrite anything that's already in the462        # class?  The default is to not overwrite a function that already463        # exists.464        if unconditional_add:465            self.unconditional_adds[name] = True466 467        self.names.append(name)468 469        if annotation_fields is not None:470            self.method_annotations[name] = (annotation_fields, return_type)471 472        args = ','.join(args)473        body = '\n'.join(body)474 475        # Compute the text of the entire function, add it to the text we're generating.476        self.src.append(f'{f' {decorator}\n' if decorator else ''} def {name}({args}):\n{body}')477 478    def add_fns_to_class(self, cls):479        # The source to all of the functions we're generating.480        fns_src = '\n'.join(self.src)481 482        # The locals they use.483        local_vars = ','.join(self.locals.keys())484 485        # The names of all of the functions, used for the return value of the486        # outer function.  Need to handle the 0-tuple specially.487        if len(self.names) == 0:488            return_names = '()'489        else:490            return_names  =f'({",".join(self.names)},)'491 492        # txt is the entire function we're going to execute, including the493        # bodies of the functions we're defining.  Here's a greatly simplified494        # version:495        # def __create_fn__():496        #  def __init__(self, x, y):497        #   self.x = x498        #   self.y = y499        #  @recursive_repr500        #  def __repr__(self):501        #   return f"cls(x={self.x!r},y={self.y!r})"502        # return __init__,__repr__503 504        txt = f"def __create_fn__({local_vars}):\n{fns_src}\n return {return_names}"505        ns = {}506        exec(txt, self.globals, ns)507        fns = ns['__create_fn__'](**self.locals)508 509        # Now that we've generated the functions, assign them into cls.510        for name, fn in zip(self.names, fns):511            fn.__qualname__ = f"{cls.__qualname__}.{fn.__name__}"512 513            try:514                annotation_fields, return_type = self.method_annotations[name]515            except KeyError:516                pass517            else:518                annotate_fn = _make_annotate_function(cls, name, annotation_fields, return_type)519                fn.__annotate__ = annotate_fn520 521            if self.unconditional_adds.get(name, False):522                setattr(cls, name, fn)523            else:524                already_exists = _set_new_attribute(cls, name, fn)525 526                # See if it's an error to overwrite this particular function.527                if already_exists and (msg_extra := self.overwrite_errors.get(name)):528                    error_msg = (f'Cannot overwrite attribute {fn.__name__} '529                                 f'in class {cls.__name__}')530                    if not msg_extra is True:531                        error_msg = f'{error_msg} {msg_extra}'532 533                    raise TypeError(error_msg)534 535 536def _make_annotate_function(__class__, method_name, annotation_fields, return_type):537    # Create an __annotate__ function for a dataclass538    # Try to return annotations in the same format as they would be539    # from a regular __init__ function540 541    def __annotate__(format, /):542        Format = annotationlib.Format543        match format:544            case Format.VALUE | Format.FORWARDREF | Format.STRING:545                cls_annotations = {}546                for base in reversed(__class__.__mro__):547                    cls_annotations.update(548                        annotationlib.get_annotations(base, format=format)549                    )550 551                new_annotations = {}552                for k in annotation_fields:553                    # gh-142214: The annotation may be missing in unusual dynamic cases.554                    # If so, just skip it.555                    try:556                        new_annotations[k] = cls_annotations[k]557                    except KeyError:558                        pass559 560                if return_type is not MISSING:561                    if format == Format.STRING:562                        new_annotations["return"] = annotationlib.type_repr(return_type)563                    else:564                        new_annotations["return"] = return_type565 566                return new_annotations567 568            case _:569                raise NotImplementedError(format)570 571    # This is a flag for _add_slots to know it needs to regenerate this method572    # In order to remove references to the original class when it is replaced573    __annotate__.__generated_by_dataclasses__ = True574    __annotate__.__qualname__ = f"{__class__.__qualname__}.{method_name}.__annotate__"575 576    return __annotate__577 578 579def _field_assign(frozen, name, value, self_name):580    # If we're a frozen class, then assign to our fields in __init__581    # via object.__setattr__.  Otherwise, just use a simple582    # assignment.583    #584    # self_name is what "self" is called in this function: don't585    # hard-code "self", since that might be a field name.586    if frozen:587        return f'  __dataclass_builtins_object__.__setattr__({self_name},{name!r},{value})'588    return f'  {self_name}.{name}={value}'589 590 591def _field_init(f, frozen, globals, self_name, slots):592    # Return the text of the line in the body of __init__ that will593    # initialize this field.594 595    default_name = f'__dataclass_dflt_{f.name}__'596    if f.default_factory is not MISSING:597        if f.init:598            # This field has a default factory.  If a parameter is599            # given, use it.  If not, call the factory.600            globals[default_name] = f.default_factory601            value = (f'{default_name}() '602                     f'if {f.name} is __dataclass_HAS_DEFAULT_FACTORY__ '603                     f'else {f.name}')604        else:605            # This is a field that's not in the __init__ params, but606            # has a default factory function.  It needs to be607            # initialized here by calling the factory function,608            # because there's no other way to initialize it.609 610            # For a field initialized with a default=defaultvalue, the611            # class dict just has the default value612            # (cls.fieldname=defaultvalue).  But that won't work for a613            # default factory, the factory must be called in __init__614            # and we must assign that to self.fieldname.  We can't615            # fall back to the class dict's value, both because it's616            # not set, and because it might be different per-class617            # (which, after all, is why we have a factory function!).618 619            globals[default_name] = f.default_factory620            value = f'{default_name}()'621    else:622        # No default factory.623        if f.init:624            if f.default is MISSING:625                # There's no default, just do an assignment.626                value = f.name627            elif f.default is not MISSING:628                globals[default_name] = f.default629                value = f.name630        else:631            # If the class has slots, then initialize this field.632            if slots and f.default is not MISSING:633                globals[default_name] = f.default634                value = default_name635            else:636                # This field does not need initialization: reading from it will637                # just use the class attribute that contains the default.638                # Signify that to the caller by returning None.639                return None640 641    # Only test this now, so that we can create variables for the642    # default.  However, return None to signify that we're not going643    # to actually do the assignment statement for InitVars.644    if f._field_type is _FIELD_INITVAR:645        return None646 647    # Now, actually generate the field assignment.648    return _field_assign(frozen, f.name, value, self_name)649 650 651def _init_param(f):652    # Return the __init__ parameter string for this field.  For653    # example, the equivalent of 'x:int=3' (except instead of 'int',654    # reference a variable set to int, and instead of '3', reference a655    # variable set to 3).656    if f.default is MISSING and f.default_factory is MISSING:657        # There's no default, and no default_factory, just output the658        # variable name and type.659        default = ''660    elif f.default is not MISSING:661        # There's a default, this will be the name that's used to look662        # it up.663        default = f'=__dataclass_dflt_{f.name}__'664    elif f.default_factory is not MISSING:665        # There's a factory function.  Set a marker.666        default = '=__dataclass_HAS_DEFAULT_FACTORY__'667    return f'{f.name}{default}'668 669 670def _init_fn(fields, std_fields, kw_only_fields, frozen, has_post_init,671             self_name, func_builder, slots):672    # fields contains both real fields and InitVar pseudo-fields.673 674    # Make sure we don't have fields without defaults following fields675    # with defaults.  This actually would be caught when exec-ing the676    # function source code, but catching it here gives a better error677    # message, and future-proofs us in case we build up the function678    # using ast.679 680    seen_default = None681    for f in std_fields:682        # Only consider the non-kw-only fields in the __init__ call.683        if f.init:684            if not (f.default is MISSING and f.default_factory is MISSING):685                seen_default = f686            elif seen_default:687                raise TypeError(f'non-default argument {f.name!r} '688                                f'follows default argument {seen_default.name!r}')689 690    annotation_fields = [f.name for f in fields if f.init]691 692    locals = {'__dataclass_HAS_DEFAULT_FACTORY__': _HAS_DEFAULT_FACTORY,693              '__dataclass_builtins_object__': object}694 695    body_lines = []696    for f in fields:697        line = _field_init(f, frozen, locals, self_name, slots)698        # line is None means that this field doesn't require699        # initialization (it's a pseudo-field).  Just skip it.700        if line:701            body_lines.append(line)702 703    # Does this class have a post-init function?704    if has_post_init:705        params_str = ','.join(f.name for f in fields706                              if f._field_type is _FIELD_INITVAR)707        body_lines.append(f'  {self_name}.{_POST_INIT_NAME}({params_str})')708 709    # If no body lines, use 'pass'.710    if not body_lines:711        body_lines = ['  pass']712 713    _init_params = [_init_param(f) for f in std_fields]714    if kw_only_fields:715        # Add the keyword-only args.  Because the * can only be added if716        # there's at least one keyword-only arg, there needs to be a test here717        # (instead of just concatenating the lists together).718        _init_params += ['*']719        _init_params += [_init_param(f) for f in kw_only_fields]720    func_builder.add_fn('__init__',721                        [self_name] + _init_params,722                        body_lines,723                        locals=locals,724                        return_type=None,725                        annotation_fields=annotation_fields)726 727 728def _frozen_get_del_attr(cls, fields, func_builder):729    locals = {'cls': cls,730              'FrozenInstanceError': FrozenInstanceError}731    condition = 'type(self) is cls'732    if fields:733        condition += ' or name in {' + ', '.join(repr(f.name) for f in fields) + '}'734 735    func_builder.add_fn('__setattr__',736                        ('self', 'name', 'value'),737                        (f'  if {condition}:',738                          '   raise FrozenInstanceError(f"cannot assign to field {name!r}")',739                         f'  super(cls, self).__setattr__(name, value)'),740                        locals=locals,741                        overwrite_error=True)742    func_builder.add_fn('__delattr__',743                        ('self', 'name'),744                        (f'  if {condition}:',745                          '   raise FrozenInstanceError(f"cannot delete field {name!r}")',746                         f'  super(cls, self).__delattr__(name)'),747                        locals=locals,748                        overwrite_error=True)749 750 751def _is_classvar(a_type, typing):752    return (a_type is typing.ClassVar753            or (typing.get_origin(a_type) is typing.ClassVar))754 755 756def _is_initvar(a_type, dataclasses):757    # The module we're checking against is the module we're758    # currently in (dataclasses.py).759    return (a_type is dataclasses.InitVar760            or type(a_type) is dataclasses.InitVar)761 762def _is_kw_only(a_type, dataclasses):763    return a_type is dataclasses.KW_ONLY764 765 766def _is_type(annotation, cls, a_module, a_type, is_type_predicate):767    # Given a type annotation string, does it refer to a_type in768    # a_module?  For example, when checking that annotation denotes a769    # ClassVar, then a_module is typing, and a_type is770    # typing.ClassVar.771 772    # It's possible to look up a_module given a_type, but it involves773    # looking in sys.modules (again!), and seems like a waste since774    # the caller already knows a_module.775 776    # - annotation is a string type annotation777    # - cls is the class that this annotation was found in778    # - a_module is the module we want to match779    # - a_type is the type in that module we want to match780    # - is_type_predicate is a function called with (obj, a_module)781    #   that determines if obj is of the desired type.782 783    # Since this test does not do a local namespace lookup (and784    # instead only a module (global) lookup), there are some things it785    # gets wrong.786 787    # With string annotations, cv0 will be detected as a ClassVar:788    #   CV = ClassVar789    #   @dataclass790    #   class C0:791    #     cv0: CV792 793    # But in this example cv1 will not be detected as a ClassVar:794    #   @dataclass795    #   class C1:796    #     CV = ClassVar797    #     cv1: CV798 799    # In C1, the code in this function (_is_type) will look up "CV" in800    # the module and not find it, so it will not consider cv1 as a801    # ClassVar.  This is a fairly obscure corner case, and the best802    # way to fix it would be to eval() the string "CV" with the803    # correct global and local namespaces.  However that would involve804    # a eval() penalty for every single field of every dataclass805    # that's defined.  It was judged not worth it.806 807    match = _MODULE_IDENTIFIER_RE.match(annotation)808    if match:809        ns = None810        module_name = match.group(1)811        if not module_name:812            # No module name, assume the class's module did813            # "from dataclasses import InitVar".814            ns = sys.modules.get(cls.__module__).__dict__815        else:816            # Look up module_name in the class's module.817            module = sys.modules.get(cls.__module__)818            if module and module.__dict__.get(module_name) is a_module:819                ns = sys.modules.get(a_type.__module__).__dict__820        if ns and is_type_predicate(ns.get(match.group(2)), a_module):821            return True822    return False823 824 825def _get_field(cls, a_name, a_type, default_kw_only):826    # Return a Field object for this field name and type.  ClassVars and827    # InitVars are also returned, but marked as such (see f._field_type).828    # default_kw_only is the value of kw_only to use if there isn't a field()829    # that defines it.830 831    # If the default value isn't derived from Field, then it's only a832    # normal default value.  Convert it to a Field().833    default = getattr(cls, a_name, MISSING)834    if isinstance(default, Field):835        f = default836    else:837        if isinstance(default, types.MemberDescriptorType):838            # This is a field in __slots__, so it has no default value.839            default = MISSING840        f = field(default=default)841 842    # Only at this point do we know the name and the type.  Set them.843    f.name = a_name844    f.type = a_type845 846    # Assume it's a normal field until proven otherwise.  We're next847    # going to decide if it's a ClassVar or InitVar, everything else848    # is just a normal field.849    f._field_type = _FIELD850 851    # In addition to checking for actual types here, also check for852    # string annotations.  get_type_hints() won't always work for us853    # (see https://github.com/python/typing/issues/508 for example),854    # plus it's expensive and would require an eval for every string855    # annotation.  So, make a best effort to see if this is a ClassVar856    # or InitVar using regex's and checking that the thing referenced857    # is actually of the correct type.858 859    # For the complete discussion, see https://bugs.python.org/issue33453860 861    # If typing has not been imported, then it's impossible for any862    # annotation to be a ClassVar.  So, only look for ClassVar if863    # typing has been imported by any module (not necessarily cls's864    # module).865    typing = sys.modules.get('typing')866    if typing:867        if (_is_classvar(a_type, typing)868            or (isinstance(f.type, str)869                and _is_type(f.type, cls, typing, typing.ClassVar,870                             _is_classvar))):871            f._field_type = _FIELD_CLASSVAR872 873    # If the type is InitVar, or if it's a matching string annotation,874    # then it's an InitVar.875    if f._field_type is _FIELD:876        # The module we're checking against is the module we're877        # currently in (dataclasses.py).878        dataclasses = sys.modules[__name__]879        if (_is_initvar(a_type, dataclasses)880            or (isinstance(f.type, str)881                and _is_type(f.type, cls, dataclasses, dataclasses.InitVar,882                             _is_initvar))):883            f._field_type = _FIELD_INITVAR884 885    # Validations for individual fields.  This is delayed until now,886    # instead of in the Field() constructor, since only here do we887    # know the field name, which allows for better error reporting.888 889    # Special restrictions for ClassVar and InitVar.890    if f._field_type in (_FIELD_CLASSVAR, _FIELD_INITVAR):891        if f.default_factory is not MISSING:892            raise TypeError(f'field {f.name} cannot have a '893                            'default factory')894        # Should I check for other field settings? default_factory895        # seems the most serious to check for.  Maybe add others.  For896        # example, how about init=False (or really,897        # init=<not-the-default-init-value>)?  It makes no sense for898        # ClassVar and InitVar to specify init=<anything>.899 900    # kw_only validation and assignment.901    if f._field_type in (_FIELD, _FIELD_INITVAR):902        # For real and InitVar fields, if kw_only wasn't specified use the903        # default value.904        if f.kw_only is MISSING:905            f.kw_only = default_kw_only906    else:907        # Make sure kw_only isn't set for ClassVars908        assert f._field_type is _FIELD_CLASSVAR909        if f.kw_only is not MISSING:910            raise TypeError(f'field {f.name} is a ClassVar but specifies '911                            'kw_only')912 913    # For real fields, disallow mutable defaults.  Use unhashable as a proxy914    # indicator for mutability.  Read the __hash__ attribute from the class,915    # not the instance.916    if f._field_type is _FIELD and f.default.__class__.__hash__ is None:917        raise ValueError(f'mutable default {type(f.default)} for field '918                         f'{f.name} is not allowed: use default_factory')919 920    return f921 922def _set_new_attribute(cls, name, value):923    # Never overwrites an existing attribute.  Returns True if the924    # attribute already exists.925    if name in cls.__dict__:926        return True927    setattr(cls, name, value)928    return False929 930 931# Decide if/how we're going to create a hash function.  Key is932# (unsafe_hash, eq, frozen, does-hash-exist).  Value is the action to933# take.  The common case is to do nothing, so instead of providing a934# function that is a no-op, use None to signify that.935 936def _hash_set_none(cls, fields, func_builder):937    # It's sort of a hack that I'm setting this here, instead of at938    # func_builder.add_fns_to_class time, but since this is an exceptional case939    # (it's not setting an attribute to a function, but to a scalar value),940    # just do it directly here.  I might come to regret this.941    cls.__hash__ = None942 943def _hash_add(cls, fields, func_builder):944    flds = [f for f in fields if (f.compare if f.hash is None else f.hash)]945    self_tuple = _tuple_str('self', flds)946    func_builder.add_fn('__hash__',947                        ('self',),948                        [f'  return hash({self_tuple})'],949                        unconditional_add=True)950 951def _hash_exception(cls, fields, func_builder):952    # Raise an exception.953    raise TypeError(f'Cannot overwrite attribute __hash__ '954                    f'in class {cls.__name__}')955 956#957#                +-------------------------------------- unsafe_hash?958#                |      +------------------------------- eq?959#                |      |      +------------------------ frozen?960#                |      |      |      +----------------  has-explicit-hash?961#                |      |      |      |962#                |      |      |      |        +-------  action963#                |      |      |      |        |964#                v      v      v      v        v965_hash_action = {(False, False, False, False): None,966                (False, False, False, True ): None,967                (False, False, True,  False): None,968                (False, False, True,  True ): None,969                (False, True,  False, False): _hash_set_none,970                (False, True,  False, True ): None,971                (False, True,  True,  False): _hash_add,972                (False, True,  True,  True ): None,973                (True,  False, False, False): _hash_add,974                (True,  False, False, True ): _hash_exception,975                (True,  False, True,  False): _hash_add,976                (True,  False, True,  True ): _hash_exception,977                (True,  True,  False, False): _hash_add,978                (True,  True,  False, True ): _hash_exception,979                (True,  True,  True,  False): _hash_add,980                (True,  True,  True,  True ): _hash_exception,981                }982# See https://bugs.python.org/issue32929#msg312829 for an if-statement983# version of this table.984 985 986def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,987                   match_args, kw_only, slots, weakref_slot):988    # Now that dicts retain insertion order, there's no reason to use989    # an ordered dict.  I am leveraging that ordering here, because990    # derived class fields overwrite base class fields, but the order991    # is defined by the base class, which is found first.992    fields = {}993 994    if cls.__module__ in sys.modules:995        globals = sys.modules[cls.__module__].__dict__996    else:997        # Theoretically this can happen if someone writes998        # a custom string to cls.__module__.  In which case999        # such dataclass won't be fully introspectable1000        # (w.r.t. typing.get_type_hints) but will still function1001        # correctly.1002        globals = {}1003 1004    setattr(cls, _PARAMS, _DataclassParams(init, repr, eq, order,1005                                           unsafe_hash, frozen,1006                                           match_args, kw_only,1007                                           slots, weakref_slot))1008 1009    # Find our base classes in reverse MRO order, and exclude1010    # ourselves.  In reversed order so that more derived classes1011    # override earlier field definitions in base classes.  As long as1012    # we're iterating over them, see if all or any of them are frozen.1013    any_frozen_base = False1014    # By default `all_frozen_bases` is `None` to represent a case,1015    # where some dataclasses does not have any bases with `_FIELDS`1016    all_frozen_bases = None1017    has_dataclass_bases = False1018    for b in cls.__mro__[-1:0:-1]:1019        # Only process classes that have been processed by our1020        # decorator.  That is, they have a _FIELDS attribute.1021        base_fields = getattr(b, _FIELDS, None)1022        if base_fields is not None:1023            has_dataclass_bases = True1024            for f in base_fields.values():1025                fields[f.name] = f1026            if all_frozen_bases is None:1027                all_frozen_bases = True1028            current_frozen = getattr(b, _PARAMS).frozen1029            all_frozen_bases = all_frozen_bases and current_frozen1030            any_frozen_base = any_frozen_base or current_frozen1031 1032    # Annotations defined specifically in this class (not in base classes).1033    #1034    # Fields are found from cls_annotations, which is guaranteed to be1035    # ordered.  Default values are from class attributes, if a field1036    # has a default.  If the default value is a Field(), then it1037    # contains additional info beyond (and possibly including) the1038    # actual default value.  Pseudo-fields ClassVars and InitVars are1039    # included, despite the fact that they're not real fields.  That's1040    # dealt with later.1041    cls_annotations = annotationlib.get_annotations(1042        cls, format=annotationlib.Format.FORWARDREF)1043 1044    # Now find fields in our class.  While doing so, validate some1045    # things, and set the default values (as class attributes) where1046    # we can.1047    cls_fields = []1048    # Get a reference to this module for the _is_kw_only() test.1049    KW_ONLY_seen = False1050    dataclasses = sys.modules[__name__]1051    for name, type in cls_annotations.items():1052        # See if this is a marker to change the value of kw_only.1053        if (_is_kw_only(type, dataclasses)1054            or (isinstance(type, str)1055                and _is_type(type, cls, dataclasses, dataclasses.KW_ONLY,1056                             _is_kw_only))):1057            # Switch the default to kw_only=True, and ignore this1058            # annotation: it's not a real field.1059            if KW_ONLY_seen:1060                raise TypeError(f'{name!r} is KW_ONLY, but KW_ONLY '1061                                'has already been specified')1062            KW_ONLY_seen = True1063            kw_only = True1064        else:1065            # Otherwise it's a field of some type.1066            cls_fields.append(_get_field(cls, name, type, kw_only))1067 1068    for f in cls_fields:1069        fields[f.name] = f1070 1071        # If the class attribute (which is the default value for this1072        # field) exists and is of type 'Field', replace it with the1073        # real default.  This is so that normal class introspection1074        # sees a real default value, not a Field.1075        if isinstance(getattr(cls, f.name, None), Field):1076            if f.default is MISSING:1077                # If there's no default, delete the class attribute.1078                # This happens if we specify field(repr=False), for1079                # example (that is, we specified a field object, but1080                # no default value).  Also if we're using a default1081                # factory.  The class attribute should not be set at1082                # all in the post-processed class.1083                delattr(cls, f.name)1084            else:1085                setattr(cls, f.name, f.default)1086 1087    # Do we have any Field members that don't also have annotations?1088    for name, value in cls.__dict__.items():1089        if isinstance(value, Field) and not name in cls_annotations:1090            raise TypeError(f'{name!r} is a field but has no type annotation')1091 1092    # Check rules that apply if we are derived from any dataclasses.1093    if has_dataclass_bases:1094        # Raise an exception if any of our bases are frozen, but we're not.1095        if any_frozen_base and not frozen:1096            raise TypeError('cannot inherit non-frozen dataclass from a '1097                            'frozen one')1098 1099        # Raise an exception if we're frozen, but none of our bases are.1100        if all_frozen_bases is False and frozen:1101            raise TypeError('cannot inherit frozen dataclass from a '1102                            'non-frozen one')1103 1104    # Remember all of the fields on our class (including bases).  This1105    # also marks this class as being a dataclass.1106    setattr(cls, _FIELDS, fields)1107 1108    # Was this class defined with an explicit __hash__?  Note that if1109    # __eq__ is defined in this class, then python will automatically1110    # set __hash__ to None.  This is a heuristic, as it's possible1111    # that such a __hash__ == None was not auto-generated, but it's1112    # close enough.1113    class_hash = cls.__dict__.get('__hash__', MISSING)1114    has_explicit_hash = not (class_hash is MISSING or1115                             (class_hash is None and '__eq__' in cls.__dict__))1116 1117    # If we're generating ordering methods, we must be generating the1118    # eq methods.1119    if order and not eq:1120        raise ValueError('eq must be true if order is true')1121 1122    # Include InitVars and regular fields (so, not ClassVars).  This is1123    # initialized here, outside of the "if init:" test, because std_init_fields1124    # is used with match_args, below.1125    all_init_fields = [f for f in fields.values()1126                       if f._field_type in (_FIELD, _FIELD_INITVAR)]1127    (std_init_fields,1128     kw_only_init_fields) = _fields_in_init_order(all_init_fields)1129 1130    func_builder = _FuncBuilder(globals)1131 1132    if init:1133        # Does this class have a post-init function?1134        has_post_init = hasattr(cls, _POST_INIT_NAME)1135 1136        _init_fn(all_init_fields,1137                 std_init_fields,1138                 kw_only_init_fields,1139                 frozen,1140                 has_post_init,1141                 # The name to use for the "self"1142                 # param in __init__.  Use "self"1143                 # if possible.1144                 '__dataclass_self__' if 'self' in fields1145                 else 'self',1146                 func_builder,1147                 slots,1148                 )1149 1150    _set_new_attribute(cls, '__replace__', _replace)1151 1152    # Get the fields as a list, and include only real fields.  This is1153    # used in all of the following methods.1154    field_list = [f for f in fields.values() if f._field_type is _FIELD]1155 1156    if repr:1157        flds = [f for f in field_list if f.repr]1158        func_builder.add_fn('__repr__',1159                            ('self',),1160                            ['  return f"{self.__class__.__qualname__}(' +1161                             ', '.join([f"{f.name}={{self.{f.name}!r}}"1162                                        for f in flds]) + ')"'],1163                            locals={'__dataclasses_recursive_repr': recursive_repr},1164                            decorator="@__dataclasses_recursive_repr()")1165 1166    if eq:1167        # Create __eq__ method.  There's no need for a __ne__ method,1168        # since python will call __eq__ and negate it.1169        cmp_fields = (field for field in field_list if field.compare)1170        terms = [f'self.{field.name}==other.{field.name}' for field in cmp_fields]1171        field_comparisons = ' and '.join(terms) or 'True'1172        func_builder.add_fn('__eq__',1173                            ('self', 'other'),1174                            [ '  if self is other:',1175                              '   return True',1176                              '  if other.__class__ is self.__class__:',1177                             f'   return {field_comparisons}',1178                              '  return NotImplemented'])1179 1180    if order:1181        # Create and set the ordering methods.1182        flds = [f for f in field_list if f.compare]1183        self_tuple = _tuple_str('self', flds)1184        other_tuple = _tuple_str('other', flds)1185        for name, op in [('__lt__', '<'),1186                         ('__le__', '<='),1187                         ('__gt__', '>'),1188                         ('__ge__', '>='),1189                         ]:1190            # Create a comparison function.  If the fields in the object are1191            # named 'x' and 'y', then self_tuple is the string1192            # '(self.x,self.y)' and other_tuple is the string1193            # '(other.x,other.y)'.1194            func_builder.add_fn(name,1195                            ('self', 'other'),1196                            [ '  if other.__class__ is self.__class__:',1197                             f'   return {self_tuple}{op}{other_tuple}',1198                              '  return NotImplemented'],1199                            overwrite_error='Consider using functools.total_ordering')1200 

Showing the first 1,200 of 1805 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai