# orm/decl_base.py# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors# <see AUTHORS file>## This module is part of SQLAlchemy and is released under# the MIT License: https://www.opensource.org/licenses/mit-license.php"""Internal implementation for declarative."""from__future__importannotationsimportcollectionsimportdataclassesimportrefromtypingimportAnyfromtypingimportCallablefromtypingimportcastfromtypingimportDictfromtypingimportIterablefromtypingimportListfromtypingimportMappingfromtypingimportNamedTuplefromtypingimportNoReturnfromtypingimportOptionalfromtypingimportSequencefromtypingimportTuplefromtypingimportTypefromtypingimportTYPE_CHECKINGfromtypingimportTypeVarfromtypingimportUnionimportweakreffrom.importattributesfrom.importclsregistryfrom.importexcasorm_excfrom.importinstrumentationfrom.importmapperlibfrom._typingimport_Ofrom._typingimportattr_is_internal_proxyfrom.attributesimportInstrumentedAttributefrom.attributesimportQueryableAttributefrom.baseimport_is_mapped_classfrom.baseimportInspectionAttrfrom.descriptor_propsimportCompositePropertyfrom.descriptor_propsimportSynonymPropertyfrom.interfacesimport_AttributeOptionsfrom.interfacesimport_DCAttributeOptionsfrom.interfacesimport_IntrospectsAnnotationsfrom.interfacesimport_MappedAttributefrom.interfacesimport_MapsColumnsfrom.interfacesimportMapperPropertyfrom.mapperimportMapperfrom.propertiesimportColumnPropertyfrom.propertiesimportMappedColumnfrom.utilimport_extract_mapped_subtypefrom.utilimport_is_mapped_annotationfrom.utilimportclass_mapperfrom.utilimportde_stringify_annotationfrom..importeventfrom..importexcfrom..importutilfrom..sqlimportexpressionfrom..sql.baseimport_NoArgfrom..sql.schemaimportColumnfrom..sql.schemaimportTablefrom..utilimporttopologicalfrom..util.typingimport_AnnotationScanTypefrom..util.typingimportget_argsfrom..util.typingimportis_fwd_reffrom..util.typingimportis_literalfrom..util.typingimportProtocolfrom..util.typingimportTypedDictifTYPE_CHECKING:from._typingimport_ClassDictfrom._typingimport_RegistryTypefrom.baseimportMappedfrom.decl_apiimportdeclared_attrfrom.instrumentationimportClassManagerfrom..sql.elementsimportNamedColumnfrom..sql.schemaimportMetaDatafrom..sql.selectableimportFromClause_T=TypeVar("_T",bound=Any)_MapperKwArgs=Mapping[str,Any]_TableArgsType=Union[Tuple[Any,...],Dict[str,Any]]classMappedClassProtocol(Protocol[_O]):"""A protocol representing a SQLAlchemy mapped class. The protocol is generic on the type of class, use ``MappedClassProtocol[Any]`` to allow any mapped class. """__name__:str__mapper__:Mapper[_O]__table__:FromClausedef__call__(self,**kw:Any)->_O:...class_DeclMappedClassProtocol(MappedClassProtocol[_O],Protocol):"Internal more detailed version of ``MappedClassProtocol``."metadata:MetaData__tablename__:str__mapper_args__:_MapperKwArgs__table_args__:Optional[_TableArgsType]_sa_apply_dc_transforms:Optional[_DataclassArguments]def__declare_first__(self)->None:...def__declare_last__(self)->None:...class_DataclassArguments(TypedDict):init:Union[_NoArg,bool]repr:Union[_NoArg,bool]eq:Union[_NoArg,bool]order:Union[_NoArg,bool]unsafe_hash:Union[_NoArg,bool]match_args:Union[_NoArg,bool]kw_only:Union[_NoArg,bool]dataclass_callable:Union[_NoArg,Callable[...,Type[Any]]]def_declared_mapping_info(cls:Type[Any],)->Optional[Union[_DeferredMapperConfig,Mapper[Any]]]:# deferred mappingif_DeferredMapperConfig.has_cls(cls):return_DeferredMapperConfig.config_for_cls(cls)# regular mappingelif_is_mapped_class(cls):returnclass_mapper(cls,configure=False)else:returnNonedef_is_supercls_for_inherits(cls:Type[Any])->bool:"""return True if this class will be used as a superclass to set in 'inherits'. This includes deferred mapper configs that aren't mapped yet, however does not include classes with _sa_decl_prepare_nocascade (e.g. ``AbstractConcreteBase``); these concrete-only classes are not set up as "inherits" until after mappers are configured using mapper._set_concrete_base() """if_DeferredMapperConfig.has_cls(cls):returnnot_get_immediate_cls_attr(cls,"_sa_decl_prepare_nocascade",strict=True)# regular mappingelif_is_mapped_class(cls):returnTrueelse:returnFalsedef_resolve_for_abstract_or_classical(cls:Type[Any])->Optional[Type[Any]]:ifclsisobject:returnNonesup:Optional[Type[Any]]ifcls.__dict__.get("__abstract__",False):forbase_incls.__bases__:sup=_resolve_for_abstract_or_classical(base_)ifsupisnotNone:returnsupelse:returnNoneelse:clsmanager=_dive_for_cls_manager(cls)ifclsmanager:returnclsmanager.class_else:returnclsdef_get_immediate_cls_attr(cls:Type[Any],attrname:str,strict:bool=False)->Optional[Any]:"""return an attribute of the class that is either present directly on the class, e.g. not on a superclass, or is from a superclass but this superclass is a non-mapped mixin, that is, not a descendant of the declarative base and is also not classically mapped. This is used to detect attributes that indicate something about a mapped class independently from any mapped classes that it may inherit from. """# the rules are different for this name than others,# make sure we've moved it out. transitionalassertattrname!="__abstract__"ifnotissubclass(cls,object):returnNoneifattrnameincls.__dict__:returngetattr(cls,attrname)forbaseincls.__mro__[1:]:_is_classical_inherits=_dive_for_cls_manager(base)isnotNoneifattrnameinbase.__dict__and(baseisclsor((baseincls.__bases__ifstrictelseTrue)andnot_is_classical_inherits)):returngetattr(base,attrname)else:returnNonedef_dive_for_cls_manager(cls:Type[_O])->Optional[ClassManager[_O]]:# because the class manager registration is pluggable,# we need to do the search for every class in the hierarchy,# rather than just a simple "cls._sa_class_manager"forbaseincls.__mro__:manager:Optional[ClassManager[_O]]=attributes.opt_manager_of_class(base)ifmanager:returnmanagerreturnNonedef_as_declarative(registry:_RegistryType,cls:Type[Any],dict_:_ClassDict)->Optional[_MapperConfig]:# declarative scans the class for attributes. no table or mapper# args passed separately.return_MapperConfig.setup_mapping(registry,cls,dict_,None,{})def_mapper(registry:_RegistryType,cls:Type[_O],table:Optional[FromClause],mapper_kw:_MapperKwArgs,)->Mapper[_O]:_ImperativeMapperConfig(registry,cls,table,mapper_kw)returncast("MappedClassProtocol[_O]",cls).__mapper__@util.preload_module("sqlalchemy.orm.decl_api")def_is_declarative_props(obj:Any)->bool:_declared_attr_common=util.preloaded.orm_decl_api._declared_attr_commonreturnisinstance(obj,(_declared_attr_common,util.classproperty))def_check_declared_props_nocascade(obj:Any,name:str,cls:Type[_O])->bool:if_is_declarative_props(obj):ifgetattr(obj,"_cascading",False):util.warn("@declared_attr.cascading is not supported on the %s ""attribute on class %s. This attribute invokes for ""subclasses in any case."%(name,cls))returnTrueelse:returnFalseclass_MapperConfig:__slots__=("cls","classname","properties","declared_attr_reg","__weakref__",)cls:Type[Any]classname:strproperties:util.OrderedDict[str,Union[Sequence[NamedColumn[Any]],NamedColumn[Any],MapperProperty[Any]],]declared_attr_reg:Dict[declared_attr[Any],Any]@classmethoddefsetup_mapping(cls,registry:_RegistryType,cls_:Type[_O],dict_:_ClassDict,table:Optional[FromClause],mapper_kw:_MapperKwArgs,)->Optional[_MapperConfig]:manager=attributes.opt_manager_of_class(cls)ifmanagerandmanager.class_iscls_:raiseexc.InvalidRequestError(f"Class {cls!r} already has been instrumented declaratively")ifcls_.__dict__.get("__abstract__",False):returnNonedefer_map=_get_immediate_cls_attr(cls_,"_sa_decl_prepare_nocascade",strict=True)orhasattr(cls_,"_sa_decl_prepare")ifdefer_map:return_DeferredMapperConfig(registry,cls_,dict_,table,mapper_kw)else:return_ClassScanMapperConfig(registry,cls_,dict_,table,mapper_kw)def__init__(self,registry:_RegistryType,cls_:Type[Any],mapper_kw:_MapperKwArgs,):self.cls=util.assert_arg_type(cls_,type,"cls_")self.classname=cls_.__name__self.properties=util.OrderedDict()self.declared_attr_reg={}ifnotmapper_kw.get("non_primary",False):instrumentation.register_class(self.cls,finalize=False,registry=registry,declarative_scan=self,init_method=registry.constructor,)else:manager=attributes.opt_manager_of_class(self.cls)ifnotmanagerornotmanager.is_mapped:raiseexc.InvalidRequestError("Class %s has no primary mapper configured. Configure ""a primary mapper first before setting up a non primary ""Mapper."%self.cls)defset_cls_attribute(self,attrname:str,value:_T)->_T:manager=instrumentation.manager_of_class(self.cls)manager.install_member(attrname,value)returnvaluedefmap(self,mapper_kw:_MapperKwArgs=...)->Mapper[Any]:raiseNotImplementedError()def_early_mapping(self,mapper_kw:_MapperKwArgs)->None:self.map(mapper_kw)class_ImperativeMapperConfig(_MapperConfig):__slots__=("local_table","inherits")def__init__(self,registry:_RegistryType,cls_:Type[_O],table:Optional[FromClause],mapper_kw:_MapperKwArgs,):super().__init__(registry,cls_,mapper_kw)self.local_table=self.set_cls_attribute("__table__",table)withmapperlib._CONFIGURE_MUTEX:ifnotmapper_kw.get("non_primary",False):clsregistry.add_class(self.classname,self.cls,registry._class_registry)self._setup_inheritance(mapper_kw)self._early_mapping(mapper_kw)defmap(self,mapper_kw:_MapperKwArgs=util.EMPTY_DICT)->Mapper[Any]:mapper_cls=Mapperreturnself.set_cls_attribute("__mapper__",mapper_cls(self.cls,self.local_table,**mapper_kw),)def_setup_inheritance(self,mapper_kw:_MapperKwArgs)->None:cls=self.clsinherits=mapper_kw.get("inherits",None)ifinheritsisNone:# since we search for classical mappings now, search for# multiple mapped bases as well and raise an error.inherits_search=[]forbase_incls.__bases__:c=_resolve_for_abstract_or_classical(base_)ifcisNone:continueif_is_supercls_for_inherits(c)andcnotininherits_search:inherits_search.append(c)ifinherits_search:iflen(inherits_search)>1:raiseexc.InvalidRequestError("Class %s has multiple mapped bases: %r"%(cls,inherits_search))inherits=inherits_search[0]elifisinstance(inherits,Mapper):inherits=inherits.class_self.inherits=inheritsclass_CollectedAnnotation(NamedTuple):raw_annotation:_AnnotationScanTypemapped_container:Optional[Type[Mapped[Any]]]extracted_mapped_annotation:Union[_AnnotationScanType,str]is_dataclass:boolattr_value:Anyoriginating_module:stroriginating_class:Type[Any]class_ClassScanMapperConfig(_MapperConfig):__slots__=("registry","clsdict_view","collected_attributes","collected_annotations","local_table","persist_selectable","declared_columns","column_ordering","column_copies","table_args","tablename","mapper_args","mapper_args_fn","table_fn","inherits","single","allow_dataclass_fields","dataclass_setup_arguments","is_dataclass_prior_to_mapping","allow_unmapped_annotations",)is_deferred=Falseregistry:_RegistryTypeclsdict_view:_ClassDictcollected_annotations:Dict[str,_CollectedAnnotation]collected_attributes:Dict[str,Any]local_table:Optional[FromClause]persist_selectable:Optional[FromClause]declared_columns:util.OrderedSet[Column[Any]]column_ordering:Dict[Column[Any],int]column_copies:Dict[Union[MappedColumn[Any],Column[Any]],Union[MappedColumn[Any],Column[Any]],]tablename:Optional[str]mapper_args:Mapping[str,Any]table_args:Optional[_TableArgsType]mapper_args_fn:Optional[Callable[[],Dict[str,Any]]]inherits:Optional[Type[Any]]single:boolis_dataclass_prior_to_mapping:boolallow_unmapped_annotations:booldataclass_setup_arguments:Optional[_DataclassArguments]"""if the class has SQLAlchemy native dataclass parameters, where we will turn the class into a dataclass within the declarative mapping process. """allow_dataclass_fields:bool"""if true, look for dataclass-processed Field objects on the target class as well as superclasses and extract ORM mapping directives from the "metadata" attribute of each Field. if False, dataclass fields can still be used, however they won't be mapped. """def__init__(self,registry:_RegistryType,cls_:Type[_O],dict_:_ClassDict,table:Optional[FromClause],mapper_kw:_MapperKwArgs,):# grab class dict before the instrumentation manager has been added.# reduces cyclesself.clsdict_view=(util.immutabledict(dict_)ifdict_elseutil.EMPTY_DICT)super().__init__(registry,cls_,mapper_kw)self.registry=registryself.persist_selectable=Noneself.collected_attributes={}self.collected_annotations={}self.declared_columns=util.OrderedSet()self.column_ordering={}self.column_copies={}self.single=Falseself.dataclass_setup_arguments=dca=getattr(self.cls,"_sa_apply_dc_transforms",None)self.allow_unmapped_annotations=getattr(self.cls,"__allow_unmapped__",False)orbool(self.dataclass_setup_arguments)self.is_dataclass_prior_to_mapping=cld=dataclasses.is_dataclass(cls_)sdk=_get_immediate_cls_attr(cls_,"__sa_dataclass_metadata_key__")# we don't want to consume Field objects from a not-already-dataclass.# the Field objects won't have their "name" or "type" populated,# and while it seems like we could just set these on Field as we# read them, Field is documented as "user read only" and we need to# stay far away from any off-label use of dataclasses APIs.if(notcldordca)andsdk:raiseexc.InvalidRequestError("SQLAlchemy mapped dataclasses can't consume mapping ""information from dataclass.Field() objects if the immediate ""class is not already a dataclass.")# if already a dataclass, and __sa_dataclass_metadata_key__ present,# then also look inside of dataclass.Field() objects yielded by# dataclasses.get_fields(cls) when scanning for attributesself.allow_dataclass_fields=bool(sdkandcld)self._setup_declared_events()self._scan_attributes()self._setup_dataclasses_transforms()withmapperlib._CONFIGURE_MUTEX:clsregistry.add_class(self.classname,self.cls,registry._class_registry)self._setup_inheriting_mapper(mapper_kw)self._extract_mappable_attributes()self._extract_declared_columns()self._setup_table(table)self._setup_inheriting_columns(mapper_kw)self._early_mapping(mapper_kw)def_setup_declared_events(self)->None:if_get_immediate_cls_attr(self.cls,"__declare_last__"):@event.listens_for(Mapper,"after_configured")defafter_configured()->None:cast("_DeclMappedClassProtocol[Any]",self.cls).__declare_last__()if_get_immediate_cls_attr(self.cls,"__declare_first__"):@event.listens_for(Mapper,"before_configured")defbefore_configured()->None:cast("_DeclMappedClassProtocol[Any]",self.cls).__declare_first__()def_cls_attr_override_checker(self,cls:Type[_O])->Callable[[str,Any],bool]:"""Produce a function that checks if a class has overridden an attribute, taking SQLAlchemy-enabled dataclass fields into account. """ifself.allow_dataclass_fields:sa_dataclass_metadata_key=_get_immediate_cls_attr(cls,"__sa_dataclass_metadata_key__")else:sa_dataclass_metadata_key=Noneifnotsa_dataclass_metadata_key:defattribute_is_overridden(key:str,obj:Any)->bool:returngetattr(cls,key,obj)isnotobjelse:all_datacls_fields={f.name:f.metadata[sa_dataclass_metadata_key]forfinutil.dataclass_fields(cls)ifsa_dataclass_metadata_keyinf.metadata}local_datacls_fields={f.name:f.metadata[sa_dataclass_metadata_key]forfinutil.local_dataclass_fields(cls)ifsa_dataclass_metadata_keyinf.metadata}absent=object()defattribute_is_overridden(key:str,obj:Any)->bool:if_is_declarative_props(obj):obj=obj.fget# this function likely has some failure modes still if# someone is doing a deep mixing of the same attribute# name as plain Python attribute vs. dataclass field.ret=local_datacls_fields.get(key,absent)if_is_declarative_props(ret):ret=ret.fgetifretisobj:returnFalseelifretisnotabsent:returnTrueall_field=all_datacls_fields.get(key,absent)ret=getattr(cls,key,obj)ifretisobj:returnFalse# for dataclasses, this could be the# 'default' of the field. so filter more specifically# for an already-mapped InstrumentedAttributeifretisnotabsentandisinstance(ret,InstrumentedAttribute):returnTrueifall_fieldisobj:returnFalseelifall_fieldisnotabsent:returnTrue# can't find another attributereturnFalsereturnattribute_is_overridden_include_dunders={"__table__","__mapper_args__","__tablename__","__table_args__",}_match_exclude_dunders=re.compile(r"^(?:_sa_|__)")def_cls_attr_resolver(self,cls:Type[Any])->Callable[[],Iterable[Tuple[str,Any,Any,bool]]]:"""produce a function to iterate the "attributes" of a class which we want to consider for mapping, adjusting for SQLAlchemy fields embedded in dataclass fields. """cls_annotations=util.get_annotations(cls)cls_vars=vars(cls)_include_dunders=self._include_dunders_match_exclude_dunders=self._match_exclude_dundersnames=[nforninutil.merge_lists_w_ordering(list(cls_vars),list(cls_annotations))ifnot_match_exclude_dunders.match(n)ornin_include_dunders]ifself.allow_dataclass_fields:sa_dataclass_metadata_key:Optional[str]=_get_immediate_cls_attr(cls,"__sa_dataclass_metadata_key__")else:sa_dataclass_metadata_key=Noneifnotsa_dataclass_metadata_key:deflocal_attributes_for_class()->(Iterable[Tuple[str,Any,Any,bool]]):return((name,cls_vars.get(name),cls_annotations.get(name),False,)fornameinnames)else:dataclass_fields={field.name:fieldforfieldinutil.local_dataclass_fields(cls)}fixed_sa_dataclass_metadata_key=sa_dataclass_metadata_keydeflocal_attributes_for_class()->(Iterable[Tuple[str,Any,Any,bool]]):fornameinnames:field=dataclass_fields.get(name,None)iffieldandsa_dataclass_metadata_keyinfield.metadata:yieldfield.name,_as_dc_declaredattr(field.metadata,fixed_sa_dataclass_metadata_key),cls_annotations.get(field.name),Trueelse:yieldname,cls_vars.get(name),cls_annotations.get(name),Falsereturnlocal_attributes_for_classdef_scan_attributes(self)->None:cls=self.clscls_as_Decl=cast("_DeclMappedClassProtocol[Any]",cls)clsdict_view=self.clsdict_viewcollected_attributes=self.collected_attributescolumn_copies=self.column_copies_include_dunders=self._include_dundersmapper_args_fn=Nonetable_args=inherited_table_args=Nonetable_fn=Nonetablename=Nonefixed_table="__table__"inclsdict_viewattribute_is_overridden=self._cls_attr_override_checker(self.cls)bases=[]forbaseincls.__mro__:# collect bases and make sure standalone columns are copied# to be the column they will ultimately be on the class,# so that declared_attr functions use the right columns.# need to do this all the way up the hierarchy first# (see #8190)class_mapped=baseisnotclsand_is_supercls_for_inherits(base)local_attributes_for_class=self._cls_attr_resolver(base)ifnotclass_mappedandbaseisnotcls:locally_collected_columns=self._produce_column_copies(local_attributes_for_class,attribute_is_overridden,fixed_table,base,)else:locally_collected_columns={}bases.append((base,class_mapped,local_attributes_for_class,locally_collected_columns,))for(base,class_mapped,local_attributes_for_class,locally_collected_columns,)inbases:# this transfer can also take place as we scan each name# for finer-grained control of how collected_attributes is# populated, as this is what impacts column ordering.# however it's simpler to get it out of the way here.collected_attributes.update(locally_collected_columns)for(name,obj,annotation,is_dataclass_field,)inlocal_attributes_for_class():ifnamein_include_dunders:ifname=="__mapper_args__":check_decl=_check_declared_props_nocascade(obj,name,cls)ifnotmapper_args_fnand(notclass_mappedorcheck_decl):# don't even invoke __mapper_args__ until# after we've determined everything about the# mapped table.# make a copy of it so a class-level dictionary# is not overwritten when we update column-based# arguments.def_mapper_args_fn()->Dict[str,Any]:returndict(cls_as_Decl.__mapper_args__)mapper_args_fn=_mapper_args_fnelifname=="__tablename__":check_decl=_check_declared_props_nocascade(obj,name,cls)ifnottablenameand(notclass_mappedorcheck_decl):tablename=cls_as_Decl.__tablename__elifname=="__table__":check_decl=_check_declared_props_nocascade(obj,name,cls)# if a @declared_attr using "__table__" is detected,# wrap up a callable to look for "__table__" from# the final concrete class when we set up a table.# this was fixed by# #11509, regression in 2.0 from version 1.4.ifcheck_declandnottable_fn:# don't even invoke __table__ until we're readydef_table_fn()->FromClause:returncls_as_Decl.__table__table_fn=_table_fnelifname=="__table_args__":check_decl=_check_declared_props_nocascade(obj,name,cls)ifnottable_argsand(notclass_mappedorcheck_decl):table_args=cls_as_Decl.__table_args__ifnotisinstance(table_args,(tuple,dict,type(None))):raiseexc.ArgumentError("__table_args__ value must be a tuple, ""dict, or None")ifbaseisnotcls:inherited_table_args=Trueelse:# any other dunder names; should not be here# as we have tested for all four names in# _include_dundersassertFalseelifclass_mapped:if_is_declarative_props(obj)andnotobj._quiet:util.warn("Regular (i.e. not __special__) ""attribute '%s.%s' uses @declared_attr, ""but owning class %s is mapped - ""not applying to subclass %s."%(base.__name__,name,base,cls))continueelifbaseisnotcls:# we're a mixin, abstract base, or something that is# acting like that for now.ifisinstance(obj,(Column,MappedColumn)):# already copied columns to the mapped class.continueelifisinstance(obj,MapperProperty):raiseexc.InvalidRequestError("Mapper properties (i.e. deferred,""column_property(), relationship(), etc.) must ""be declared as @declared_attr callables ""on declarative mixin classes. For dataclass ""field() objects, use a lambda:")elif_is_declarative_props(obj):# tried to get overloads to tell this to# pylance, no luckassertobjisnotNoneifobj._cascading:ifnameinclsdict_view:# unfortunately, while we can use the user-# defined attribute here to allow a clean# override, if there's another# subclass below then it still tries to use# this. not sure if there is enough# information here to add this as a feature# later on.util.warn("Attribute '%s' on class %s cannot be ""processed due to ""@declared_attr.cascading; ""skipping"%(name,cls))collected_attributes[name]=column_copies[obj]=(ret)=obj.__get__(obj,cls)setattr(cls,name,ret)else:ifis_dataclass_field:# access attribute using normal class access# first, to see if it's been mapped on a# superclass. note if the dataclasses.field()# has "default", this value can be anything.ret=getattr(cls,name,None)# so, if it's anything that's not ORM# mapped, assume we should invoke the# declared_attrifnotisinstance(ret,InspectionAttr):ret=obj.fget()else:# access attribute using normal class access.# if the declared attr already took place# on a superclass that is mapped, then# this is no longer a declared_attr, it will# be the InstrumentedAttributeret=getattr(cls,name)# correct for proxies created from hybrid_property# or similar. note there is no known case that# produces nested proxies, so we are only# looking one level deep right now.if(isinstance(ret,InspectionAttr)andattr_is_internal_proxy(ret)andnotisinstance(ret.original_property,MapperProperty)):ret=ret.descriptorcollected_attributes[name]=column_copies[obj]=(ret)if(isinstance(ret,(Column,MapperProperty))andret.docisNone):ret.doc=obj.__doc__self._collect_annotation(name,obj._collect_return_annotation(),base,True,obj,)elif_is_mapped_annotation(annotation,cls,base):# Mapped annotation without any object.# product_column_copies should have handled this.# if future support for other MapperProperty,# then test if this name is already handled and# otherwise proceed to generate.ifnotfixed_table:assert(nameincollected_attributesorattribute_is_overridden(name,None))continueelse:# here, the attribute is some other kind of# property that we assume is not part of the# declarative mapping. however, check for some# more common mistakesself._warn_for_decl_attributes(base,name,obj)elifis_dataclass_fieldand(namenotinclsdict_vieworclsdict_view[name]isnotobj):# here, we are definitely looking at the target class# and not a superclass. this is currently a# dataclass-only path. if the name is only# a dataclass field and isn't in local cls.__dict__,# put the object there.# assert that the dataclass-enabled resolver agrees# with what we are seeingassertnotattribute_is_overridden(name,obj)if_is_declarative_props(obj):obj=obj.fget()collected_attributes[name]=objself._collect_annotation(name,annotation,base,False,obj)else:collected_annotation=self._collect_annotation(name,annotation,base,None,obj)is_mapped=(collected_annotationisnotNoneandcollected_annotation.mapped_containerisnotNone)generated_obj=(collected_annotation.attr_valueifcollected_annotationisnotNoneelseobj)ifobjisNoneandnotfixed_tableandis_mapped:collected_attributes[name]=(generated_objifgenerated_objisnotNoneelseMappedColumn())elifnameinclsdict_view:collected_attributes[name]=obj# else if the name is not in the cls.__dict__,# don't collect it as an attribute.# we will see the annotation only, which is meaningful# both for mapping and dataclasses setupifinherited_table_argsandnottablename:table_args=Noneself.table_args=table_argsself.tablename=tablenameself.mapper_args_fn=mapper_args_fnself.table_fn=table_fndef_setup_dataclasses_transforms(self)->None:dataclass_setup_arguments=self.dataclass_setup_argumentsifnotdataclass_setup_arguments:return# can't use is_dataclass since it uses hasattrif"__dataclass_fields__"inself.cls.__dict__:raiseexc.InvalidRequestError(f"Class {self.cls} is already a dataclass; ensure that ""base classes / decorator styles of establishing dataclasses ""are not being mixed. ""This can happen if a class that inherits from ""'MappedAsDataclass', even indirectly, is been mapped with ""'@registry.mapped_as_dataclass'")# can't create a dataclass if __table__ is already there. This would# fail an assertion when calling _get_arguments_for_make_dataclass:# assert False, "Mapped[] received without a mapping declaration"if"__table__"inself.cls.__dict__:raiseexc.InvalidRequestError(f"Class {self.cls} already defines a '__table__'. ""ORM Annotated Dataclasses do not support a pre-existing ""'__table__' element")warn_for_non_dc_attrs=collections.defaultdict(list)def_allow_dataclass_field(key:str,originating_class:Type[Any])->bool:if(originating_classisnotself.clsand"__dataclass_fields__"notinoriginating_class.__dict__):warn_for_non_dc_attrs[originating_class].append(key)returnTruemanager=instrumentation.manager_of_class(self.cls)assertmanagerisnotNonefield_list=[_AttributeOptions._get_arguments_for_make_dataclass(key,anno,mapped_container,self.collected_attributes.get(key,_NoArg.NO_ARG),)forkey,anno,mapped_containerin((key,mapped_annoifmapped_annoelseraw_anno,mapped_container,)forkey,(raw_anno,mapped_container,mapped_anno,is_dc,attr_value,originating_module,originating_class,)inself.collected_annotations.items()if_allow_dataclass_field(key,originating_class)and(keynotinself.collected_attributes# issue #9226; check for attributes that we've collected# which are already instrumented, which we would assume# mean we are in an ORM inheritance mapping and this# attribute is already mapped on the superclass. Under# no circumstance should any QueryableAttribute be sent to# the dataclass() function; anything that's mapped should# be Field and that's itornotisinstance(self.collected_attributes[key],QueryableAttribute)))]ifwarn_for_non_dc_attrs:for(originating_class,non_dc_attrs,)inwarn_for_non_dc_attrs.items():util.warn_deprecated(f"When transforming {self.cls} to a dataclass, "f"attribute(s) "f"{', '.join(repr(key)forkeyinnon_dc_attrs)} "f"originates from superclass "f"{originating_class}, which is not a dataclass. This "f"usage is deprecated and will raise an error in "f"SQLAlchemy 2.1. When declaring SQLAlchemy Declarative "f"Dataclasses, ensure that all mixin classes and other "f"superclasses which include attributes are also a "f"subclass of MappedAsDataclass.","2.0",code="dcmx",)annotations={}defaults={}foriteminfield_list:iflen(item)==2:name,tp=itemeliflen(item)==3:name,tp,spec=itemdefaults[name]=specelse:assertFalseannotations[name]=tpfork,vindefaults.items():setattr(self.cls,k,v)self._apply_dataclasses_to_any_class(dataclass_setup_arguments,self.cls,annotations)@classmethoddef_update_annotations_for_non_mapped_class(cls,klass:Type[_O])->Mapping[str,_AnnotationScanType]:cls_annotations=util.get_annotations(klass)new_anno={}forname,annotationincls_annotations.items():if_is_mapped_annotation(annotation,klass,klass):extracted=_extract_mapped_subtype(annotation,klass,klass.__module__,name,type(None),required=False,is_dataclass_field=False,expect_mapped=False,)ifextracted:inner,_=extractednew_anno[name]=innerelse:new_anno[name]=annotationreturnnew_anno@classmethoddef_apply_dataclasses_to_any_class(cls,dataclass_setup_arguments:_DataclassArguments,klass:Type[_O],use_annotations:Mapping[str,_AnnotationScanType],)->None:cls._assert_dc_arguments(dataclass_setup_arguments)dataclass_callable=dataclass_setup_arguments["dataclass_callable"]ifdataclass_callableis_NoArg.NO_ARG:dataclass_callable=dataclasses.dataclassrestored:Optional[Any]ifuse_annotations:# apply constructed annotations that should look "normal" to a# dataclasses callable, based on the fields present. This# means remove the Mapped[] container and ensure all Field# entries have an annotationrestored=getattr(klass,"__annotations__",None)klass.__annotations__=cast("Dict[str, Any]",use_annotations)else:restored=Nonetry:dataclass_callable(klass,**{k:vfork,vindataclass_setup_arguments.items()ifvisnot_NoArg.NO_ARGandk!="dataclass_callable"},)except(TypeError,ValueError)asex:raiseexc.InvalidRequestError(f"Python dataclasses error encountered when creating "f"dataclass for {klass.__name__!r}: "f"{ex!r}. Please refer to Python dataclasses ""documentation for additional information.",code="dcte",)fromexfinally:# restore original annotations outside of the dataclasses# process; for mixins and __abstract__ superclasses, SQLAlchemy# Declarative will need to see the Mapped[] container inside the# annotations in order to map subclassesifuse_annotations:ifrestoredisNone:delklass.__annotations__else:klass.__annotations__=restored@classmethoddef_assert_dc_arguments(cls,arguments:_DataclassArguments)->None:allowed={"init","repr","order","eq","unsafe_hash","kw_only","match_args","dataclass_callable",}disallowed_args=set(arguments).difference(allowed)ifdisallowed_args:msg=", ".join(f"{arg!r}"forarginsorted(disallowed_args))raiseexc.ArgumentError(f"Dataclass argument(s) {msg} are not accepted")def_collect_annotation(self,name:str,raw_annotation:_AnnotationScanType,originating_class:Type[Any],expect_mapped:Optional[bool],attr_value:Any,)->Optional[_CollectedAnnotation]:ifnameinself.collected_annotations:returnself.collected_annotations[name]ifraw_annotationisNone:returnNoneis_dataclass=self.is_dataclass_prior_to_mappingallow_unmapped=self.allow_unmapped_annotationsifexpect_mappedisNone:is_dataclass_field=isinstance(attr_value,dataclasses.Field)expect_mapped=(notis_dataclass_fieldandnotallow_unmappedand(attr_valueisNoneorisinstance(attr_value,_MappedAttribute)))else:is_dataclass_field=Falseis_dataclass_field=Falseextracted=_extract_mapped_subtype(raw_annotation,self.cls,originating_class.__module__,name,type(attr_value),required=False,is_dataclass_field=is_dataclass_field,expect_mapped=expect_mappedandnotis_dataclass,)ifextractedisNone:# ClassVar can come out herereturnNoneextracted_mapped_annotation,mapped_container=extractedifattr_valueisNoneandnotis_literal(extracted_mapped_annotation):foreleminget_args(extracted_mapped_annotation):ifis_fwd_ref(elem,check_generic=True,check_for_plain_string=True):elem=de_stringify_annotation(self.cls,elem,originating_class.__module__,include_generic=True,)# look in Annotated[...] for an ORM construct,# such as Annotated[int, mapped_column(primary_key=True)]ifisinstance(elem,_IntrospectsAnnotations):attr_value=elem.found_in_pep593_annotated()self.collected_annotations[name]=ca=_CollectedAnnotation(raw_annotation,mapped_container,extracted_mapped_annotation,is_dataclass,attr_value,originating_class.__module__,originating_class,)returncadef_warn_for_decl_attributes(self,cls:Type[Any],key:str,c:Any)->None:ifisinstance(c,expression.ColumnElement):util.warn(f"Attribute '{key}' on class {cls} appears to ""be a non-schema SQLAlchemy expression ""object; this won't be part of the declarative mapping. ""To map arbitrary expressions, use ``column_property()`` ""or a similar function such as ``deferred()``, ""``query_expression()`` etc. ")def_produce_column_copies(self,attributes_for_class:Callable[[],Iterable[Tuple[str,Any,Any,bool]]],attribute_is_overridden:Callable[[str,Any],bool],fixed_table:bool,originating_class:Type[Any],)->Dict[str,Union[Column[Any],MappedColumn[Any]]]:cls=self.clsdict_=self.clsdict_viewlocally_collected_attributes={}column_copies=self.column_copies# copy mixin columns to the mapped classforname,obj,annotation,is_dataclassinattributes_for_class():if(notfixed_tableandobjisNoneand_is_mapped_annotation(annotation,cls,originating_class)):# obj is None means this is the annotation only pathifattribute_is_overridden(name,obj):# perform same "overridden" check as we do for# Column/MappedColumn, this is how a mixin col is not# applied to an inherited subclass that does not have# the mixin. the anno-only path added here for# #9564continuecollected_annotation=self._collect_annotation(name,annotation,originating_class,True,obj)obj=(collected_annotation.attr_valueifcollected_annotationisnotNoneelseobj)ifobjisNone:obj=MappedColumn()locally_collected_attributes[name]=objsetattr(cls,name,obj)elifisinstance(obj,(Column,MappedColumn)):ifattribute_is_overridden(name,obj):# if column has been overridden# (like by the InstrumentedAttribute of the# superclass), skip. don't collect the annotation# either (issue #8718)continuecollected_annotation=self._collect_annotation(name,annotation,originating_class,True,obj)obj=(collected_annotation.attr_valueifcollected_annotationisnotNoneelseobj)ifnamenotindict_andnot("__table__"indict_and(getattr(obj,"name",None)orname)indict_["__table__"].c):ifobj.foreign_keys:forfkinobj.foreign_keys:if(fk._table_columnisnotNoneandfk._table_column.tableisNone):raiseexc.InvalidRequestError("Columns with foreign keys to ""non-table-bound ""columns must be declared as ""@declared_attr callables ""on declarative mixin classes. ""For dataclass ""field() objects, use a lambda:.")column_copies[obj]=copy_=obj._copy()locally_collected_attributes[name]=copy_setattr(cls,name,copy_)returnlocally_collected_attributesdef_extract_mappable_attributes(self)->None:cls=self.clscollected_attributes=self.collected_attributesour_stuff=self.properties_include_dunders=self._include_dunderslate_mapped=_get_immediate_cls_attr(cls,"_sa_decl_prepare_nocascade",strict=True)allow_unmapped_annotations=self.allow_unmapped_annotationsexpect_annotations_wo_mapped=(allow_unmapped_annotationsorself.is_dataclass_prior_to_mapping)look_for_dataclass_things=bool(self.dataclass_setup_arguments)forkinlist(collected_attributes):ifkin_include_dunders:continuevalue=collected_attributes[k]if_is_declarative_props(value):# @declared_attr in collected_attributes only occurs here for a# @declared_attr that's directly on the mapped class;# for a mixin, these have already been evaluatedifvalue._cascading:util.warn("Use of @declared_attr.cascading only applies to ""Declarative 'mixin' and 'abstract' classes. ""Currently, this flag is ignored on mapped class ""%s"%self.cls)value=getattr(cls,k)elif(isinstance(value,QueryableAttribute)andvalue.class_isnotclsandvalue.key!=k):# detect a QueryableAttribute that's already mapped being# assigned elsewhere in userland, turn into a synonym()value=SynonymProperty(value.key)setattr(cls,k,value)if(isinstance(value,tuple)andlen(value)==1andisinstance(value[0],(Column,_MappedAttribute))):util.warn("Ignoring declarative-like tuple value of attribute ""'%s': possibly a copy-and-paste error with a comma ""accidentally placed at the end of the line?"%k)continueeliflook_for_dataclass_thingsandisinstance(value,dataclasses.Field):# we collected a dataclass Field; dataclasses would have# set up the correct state on the classcontinueelifnotisinstance(value,(Column,_DCAttributeOptions)):# using @declared_attr for some object that# isn't Column/MapperProperty/_DCAttributeOptions; remove# from the clsdict_view# and place the evaluated value onto the class.collected_attributes.pop(k)self._warn_for_decl_attributes(cls,k,value)ifnotlate_mapped:setattr(cls,k,value)continue# we expect to see the name 'metadata' in some valid cases;# however at this point we see it's assigned to something trying# to be mapped, so raise for that.# TODO: should "registry" here be also? might be too late# to change that now (2.0 betas)elifkin("metadata",):raiseexc.InvalidRequestError(f"Attribute name '{k}' is reserved when using the ""Declarative API.")elifisinstance(value,Column):_undefer_column_name(k,self.column_copies.get(value,value)# type: ignore)else:ifisinstance(value,_IntrospectsAnnotations):(annotation,mapped_container,extracted_mapped_annotation,is_dataclass,attr_value,originating_module,originating_class,)=self.collected_annotations.get(k,(None,None,None,False,None,None,None))# issue #8692 - don't do any annotation interpretation if# an annotation were present and a container such as# Mapped[] etc. were not used. If annotation is None,# do declarative_scan so that the property can raise# for requiredif(mapped_containerisnotNoneorannotationisNone# issue #10516: need to do declarative_scan even with# a non-Mapped annotation if we are doing# __allow_unmapped__, for things like col.name# assignmentorallow_unmapped_annotations):try:value.declarative_scan(self,self.registry,cls,originating_module,k,mapped_container,annotation,extracted_mapped_annotation,is_dataclass,)exceptNameErrorasne:raiseorm_exc.MappedAnnotationError(f"Could not resolve all types within mapped "f'annotation: "{annotation}". Ensure all 'f"types are written correctly and are "f"imported within the module in use.")fromneelse:# assert that we were expecting annotations# without Mapped[] were going to be passed.# otherwise an error should have been raised# by util._extract_mapped_subtype before we got here.assertexpect_annotations_wo_mappedifisinstance(value,_DCAttributeOptions):if(value._has_dataclass_argumentsandnotlook_for_dataclass_things):ifisinstance(value,MapperProperty):argnames=["init","default_factory","repr","default",]else:argnames=["init","default_factory","repr"]args={aforainargnamesifgetattr(value._attribute_options,f"dataclasses_{a}")isnot_NoArg.NO_ARG}raiseexc.ArgumentError(f"Attribute '{k}' on class {cls} includes "f"dataclasses argument(s): "f"{', '.join(sorted(repr(a)forainargs))} but "f"class does not specify ""SQLAlchemy native dataclass configuration.")ifnotisinstance(value,(MapperProperty,_MapsColumns)):# filter for _DCAttributeOptions objects that aren't# MapperProperty / mapped_column(). Currently this# includes AssociationProxy. pop it from the things# we're going to map and set it up as a descriptor# on the class.collected_attributes.pop(k)# Assoc Prox (or other descriptor object that may# use _DCAttributeOptions) is usually here, except if# 1. we're a# dataclass, dataclasses would have removed the# attr here or 2. assoc proxy is coming from a# superclass, we want it to be direct here so it# tracks state or 3. assoc prox comes from# declared_attr, uncommon casesetattr(cls,k,value)continueour_stuff[k]=valuedef_extract_declared_columns(self)->None:our_stuff=self.properties# extract columns from the class dictdeclared_columns=self.declared_columnscolumn_ordering=self.column_orderingname_to_prop_key=collections.defaultdict(set)forkey,cinlist(our_stuff.items()):ifisinstance(c,_MapsColumns):mp_to_assign=c.mapper_property_to_assignifmp_to_assign:our_stuff[key]=mp_to_assignelse:# if no mapper property to assign, this currently means# this is a MappedColumn that will produce a Column for usdelour_stuff[key]forcol,sort_orderinc.columns_to_assign:ifnotisinstance(c,CompositeProperty):name_to_prop_key[col.name].add(key)declared_columns.add(col)# we would assert this, however we want the below# warning to take effect instead. See #9630# assert col not in column_orderingcolumn_ordering[col]=sort_order# if this is a MappedColumn and the attribute key we# have is not what the column has for its key, map the# Column explicitly under the attribute key name.# otherwise, Mapper will map it under the column key.ifmp_to_assignisNoneandkey!=col.key:our_stuff[key]=colelifisinstance(c,Column):# undefer previously occurred here, and now occurs earlier.# ensure every column we get here has been namedassertc.nameisnotNonename_to_prop_key[c.name].add(key)declared_columns.add(c)# if the column is the same name as the key,# remove it from the explicit properties dict.# the normal rules for assigning column-based properties# will take over, including precedence of columns# in multi-column ColumnProperties.ifkey==c.key:delour_stuff[key]forname,keysinname_to_prop_key.items():iflen(keys)>1:util.warn("On class %r, Column object %r named ""directly multiple times, ""only one will be used: %s. ""Consider using orm.synonym instead"%(self.classname,name,(", ".join(sorted(keys)))))def_setup_table(self,table:Optional[FromClause]=None)->None:cls=self.clscls_as_Decl=cast("MappedClassProtocol[Any]",cls)tablename=self.tablenametable_args=self.table_argsclsdict_view=self.clsdict_viewdeclared_columns=self.declared_columnscolumn_ordering=self.column_orderingmanager=attributes.manager_of_class(cls)if(self.table_fnisNoneand"__table__"notinclsdict_viewandtableisNone):ifhasattr(cls,"__table_cls__"):table_cls=cast(Type[Table],util.unbound_method_to_callable(cls.__table_cls__),# type: ignore # noqa: E501)else:table_cls=TableiftablenameisnotNone:args:Tuple[Any,...]=()table_kw:Dict[str,Any]={}iftable_args:ifisinstance(table_args,dict):table_kw=table_argselifisinstance(table_args,tuple):ifisinstance(table_args[-1],dict):args,table_kw=table_args[0:-1],table_args[-1]else:args=table_argsautoload_with=clsdict_view.get("__autoload_with__")ifautoload_with:table_kw["autoload_with"]=autoload_withautoload=clsdict_view.get("__autoload__")ifautoload:table_kw["autoload"]=Truesorted_columns=sorted(declared_columns,key=lambdac:column_ordering.get(c,0),)table=self.set_cls_attribute("__table__",table_cls(tablename,self._metadata_for_cls(manager),*sorted_columns,*args,**table_kw,),)else:iftableisNone:ifself.table_fn:table=self.set_cls_attribute("__table__",self.table_fn())else:table=cls_as_Decl.__table__ifdeclared_columns:forcindeclared_columns:ifnottable.c.contains_column(c):raiseexc.ArgumentError("Can't add additional column %r when ""specifying __table__"%c.key)self.local_table=tabledef_metadata_for_cls(self,manager:ClassManager[Any])->MetaData:meta:Optional[MetaData]=getattr(self.cls,"metadata",None)ifmetaisnotNone:returnmetaelse:returnmanager.registry.metadatadef_setup_inheriting_mapper(self,mapper_kw:_MapperKwArgs)->None:cls=self.clsinherits=mapper_kw.get("inherits",None)ifinheritsisNone:# since we search for classical mappings now, search for# multiple mapped bases as well and raise an error.inherits_search=[]forbase_incls.__bases__:c=_resolve_for_abstract_or_classical(base_)ifcisNone:continueif_is_supercls_for_inherits(c)andcnotininherits_search:inherits_search.append(c)ifinherits_search:iflen(inherits_search)>1:raiseexc.InvalidRequestError("Class %s has multiple mapped bases: %r"%(cls,inherits_search))inherits=inherits_search[0]elifisinstance(inherits,Mapper):inherits=inherits.class_self.inherits=inheritsclsdict_view=self.clsdict_viewif"__table__"notinclsdict_viewandself.tablenameisNone:self.single=Truedef_setup_inheriting_columns(self,mapper_kw:_MapperKwArgs)->None:table=self.local_tablecls=self.clstable_args=self.table_argsdeclared_columns=self.declared_columnsif(tableisNoneandself.inheritsisNoneandnot_get_immediate_cls_attr(cls,"__no_table__")):raiseexc.InvalidRequestError("Class %r does not have a __table__ or __tablename__ ""specified and does not inherit from an existing ""table-mapped class."%cls)elifself.inherits:inherited_mapper_or_config=_declared_mapping_info(self.inherits)assertinherited_mapper_or_configisnotNoneinherited_table=inherited_mapper_or_config.local_tableinherited_persist_selectable=(inherited_mapper_or_config.persist_selectable)iftableisNone:# single table inheritance.# ensure no table argsiftable_args:raiseexc.ArgumentError("Can't place __table_args__ on an inherited class ""with no table.")# add any columns declared here to the inherited table.ifdeclared_columnsandnotisinstance(inherited_table,Table):raiseexc.ArgumentError(f"Can't declare columns on single-table-inherited "f"subclass {self.cls}; superclass {self.inherits} ""is not mapped to a Table")forcolindeclared_columns:assertinherited_tableisnotNoneifcol.nameininherited_table.c:ifinherited_table.c[col.name]iscol:continueraiseexc.ArgumentError(f"Column '{col}' on class {cls.__name__} "f"conflicts with existing column "f"'{inherited_table.c[col.name]}'. If using "f"Declarative, consider using the ""use_existing_column parameter of mapped_column() ""to resolve conflicts.")ifcol.primary_key:raiseexc.ArgumentError("Can't place primary key columns on an inherited ""class with no table.")ifTYPE_CHECKING:assertisinstance(inherited_table,Table)inherited_table.append_column(col)if(inherited_persist_selectableisnotNoneandinherited_persist_selectableisnotinherited_table):inherited_persist_selectable._refresh_for_new_column(col)def_prepare_mapper_arguments(self,mapper_kw:_MapperKwArgs)->None:properties=self.propertiesifself.mapper_args_fn:mapper_args=self.mapper_args_fn()else:mapper_args={}ifmapper_kw:mapper_args.update(mapper_kw)if"properties"inmapper_args:properties=dict(properties)properties.update(mapper_args["properties"])# make sure that column copies are used rather# than the original columns from any mixinsforkin("version_id_col","polymorphic_on"):ifkinmapper_args:v=mapper_args[k]mapper_args[k]=self.column_copies.get(v,v)if"primary_key"inmapper_args:mapper_args["primary_key"]=[self.column_copies.get(v,v)forvinutil.to_list(mapper_args["primary_key"])]if"inherits"inmapper_args:inherits_arg=mapper_args["inherits"]ifisinstance(inherits_arg,Mapper):inherits_arg=inherits_arg.class_ifinherits_argisnotself.inherits:raiseexc.InvalidRequestError("mapper inherits argument given for non-inheriting ""class %s"%(mapper_args["inherits"]))ifself.inherits:mapper_args["inherits"]=self.inheritsifself.inheritsandnotmapper_args.get("concrete",False):# note the superclass is expected to have a Mapper assigned and# not be a deferred config, as this is called within map()inherited_mapper=class_mapper(self.inherits,False)inherited_table=inherited_mapper.local_table# single or joined inheritance# exclude any cols on the inherited table which are# not mapped on the parent class, to avoid# mapping columns specific to sibling/nephew classesif"exclude_properties"notinmapper_args:mapper_args["exclude_properties"]=exclude_properties={c.keyforcininherited_table.cifcnotininherited_mapper._columntoproperty}.union(inherited_mapper.exclude_propertiesor())exclude_properties.difference_update([c.keyforcinself.declared_columns])# look through columns in the current mapper that# are keyed to a propname different than the colname# (if names were the same, we'd have popped it out above,# in which case the mapper makes this combination).# See if the superclass has a similar column property.# If so, join them together.fork,colinlist(properties.items()):ifnotisinstance(col,expression.ColumnElement):continueifkininherited_mapper._props:p=inherited_mapper._props[k]ifisinstance(p,ColumnProperty):# note here we place the subclass column# first. See [ticket:1892] for background.properties[k]=[col]+p.columnsresult_mapper_args=mapper_args.copy()result_mapper_args["properties"]=propertiesself.mapper_args=result_mapper_argsdefmap(self,mapper_kw:_MapperKwArgs=util.EMPTY_DICT)->Mapper[Any]:self._prepare_mapper_arguments(mapper_kw)ifhasattr(self.cls,"__mapper_cls__"):mapper_cls=cast("Type[Mapper[Any]]",util.unbound_method_to_callable(self.cls.__mapper_cls__# type: ignore),)else:mapper_cls=Mapperreturnself.set_cls_attribute("__mapper__",mapper_cls(self.cls,self.local_table,**self.mapper_args),)@util.preload_module("sqlalchemy.orm.decl_api")def_as_dc_declaredattr(field_metadata:Mapping[str,Any],sa_dataclass_metadata_key:str)->Any:# wrap lambdas inside dataclass fields inside an ad-hoc declared_attr.# we can't write it because field.metadata is immutable :( so we have# to go through extra trouble to compare thesedecl_api=util.preloaded.orm_decl_apiobj=field_metadata[sa_dataclass_metadata_key]ifcallable(obj)andnotisinstance(obj,decl_api.declared_attr):returndecl_api.declared_attr(obj)else:returnobjclass_DeferredMapperConfig(_ClassScanMapperConfig):_cls:weakref.ref[Type[Any]]is_deferred=True_configs:util.OrderedDict[weakref.ref[Type[Any]],_DeferredMapperConfig]=util.OrderedDict()def_early_mapping(self,mapper_kw:_MapperKwArgs)->None:pass# mypy disallows plain property override of variable@property# type: ignoredefcls(self)->Type[Any]:returnself._cls()# type: ignore@cls.setterdefcls(self,class_:Type[Any])->None:self._cls=weakref.ref(class_,self._remove_config_cls)self._configs[self._cls]=self@classmethoddef_remove_config_cls(cls,ref:weakref.ref[Type[Any]])->None:cls._configs.pop(ref,None)@classmethoddefhas_cls(cls,class_:Type[Any])->bool:# 2.6 fails on weakref if class_ is an old style classreturnisinstance(class_,type)andweakref.ref(class_)incls._configs@classmethoddefraise_unmapped_for_cls(cls,class_:Type[Any])->NoReturn:ifhasattr(class_,"_sa_raise_deferred_config"):class_._sa_raise_deferred_config()raiseorm_exc.UnmappedClassError(class_,msg=(f"Class {orm_exc._safe_cls_name(class_)} has a deferred ""mapping on it. It is not yet usable as a mapped class."),)@classmethoddefconfig_for_cls(cls,class_:Type[Any])->_DeferredMapperConfig:returncls._configs[weakref.ref(class_)]@classmethoddefclasses_for_base(cls,base_cls:Type[Any],sort:bool=True)->List[_DeferredMapperConfig]:classes_for_base=[mform,cls_in[(m,m.cls)formincls._configs.values()]ifcls_isnotNoneandissubclass(cls_,base_cls)]ifnotsort:returnclasses_for_baseall_m_by_cls={m.cls:mforminclasses_for_base}tuples:List[Tuple[_DeferredMapperConfig,_DeferredMapperConfig]]=[]form_clsinall_m_by_cls:tuples.extend((all_m_by_cls[base_cls],all_m_by_cls[m_cls])forbase_clsinm_cls.__bases__ifbase_clsinall_m_by_cls)returnlist(topological.sort(tuples,classes_for_base))defmap(self,mapper_kw:_MapperKwArgs=util.EMPTY_DICT)->Mapper[Any]:self._configs.pop(self._cls,None)returnsuper().map(mapper_kw)def_add_attribute(cls:Type[Any],key:str,value:MapperProperty[Any])->None:"""add an attribute to an existing declarative class. This runs through the logic to determine MapperProperty, adds it to the Mapper, adds a column to the mapped Table, etc. """if"__mapper__"incls.__dict__:mapped_cls=cast("MappedClassProtocol[Any]",cls)def_table_or_raise(mc:MappedClassProtocol[Any])->Table:ifisinstance(mc.__table__,Table):returnmc.__table__raiseexc.InvalidRequestError(f"Cannot add a new attribute to mapped class {mc.__name__!r} ""because it's not mapped against a table.")ifisinstance(value,Column):_undefer_column_name(key,value)_table_or_raise(mapped_cls).append_column(value,replace_existing=True)mapped_cls.__mapper__.add_property(key,value)elifisinstance(value,_MapsColumns):mp=value.mapper_property_to_assignforcol,_invalue.columns_to_assign:_undefer_column_name(key,col)_table_or_raise(mapped_cls).append_column(col,replace_existing=True)ifnotmp:mapped_cls.__mapper__.add_property(key,col)ifmp:mapped_cls.__mapper__.add_property(key,mp)elifisinstance(value,MapperProperty):mapped_cls.__mapper__.add_property(key,value)elifisinstance(value,QueryableAttribute)andvalue.key!=key:# detect a QueryableAttribute that's already mapped being# assigned elsewhere in userland, turn into a synonym()value=SynonymProperty(value.key)mapped_cls.__mapper__.add_property(key,value)else:type.__setattr__(cls,key,value)mapped_cls.__mapper__._expire_memoizations()else:type.__setattr__(cls,key,value)def_del_attribute(cls:Type[Any],key:str)->None:if("__mapper__"incls.__dict__andkeyincls.__dict__andnotcast("MappedClassProtocol[Any]",cls).__mapper__._dispose_called):value=cls.__dict__[key]ifisinstance(value,(Column,_MapsColumns,MapperProperty,QueryableAttribute)):raiseNotImplementedError("Can't un-map individual mapped attributes on a mapped class.")else:type.__delattr__(cls,key)cast("MappedClassProtocol[Any]",cls).__mapper__._expire_memoizations()else:type.__delattr__(cls,key)def_declarative_constructor(self:Any,**kwargs:Any)->None:"""A simple constructor that allows initialization from kwargs. Sets attributes on the constructed instance using the names and values in ``kwargs``. Only keys that are present as attributes of the instance's class are allowed. These could be, for example, any mapped columns or relationships. """cls_=type(self)forkinkwargs:ifnothasattr(cls_,k):raiseTypeError("%r is an invalid keyword argument for %s"%(k,cls_.__name__))setattr(self,k,kwargs[k])_declarative_constructor.__name__="__init__"def_undefer_column_name(key:str,column:Column[Any])->None:ifcolumn.keyisNone:column.key=keyifcolumn.nameisNone:column.name=key