U kfvi@sdZddlmZddlZddlZddlmZddlmZddlmZddlm Z ddlm Z dd lm Z dd lm Z dd lm Z dd lmZdd lmZddlmZddlmZddlmZddlmZddlmZddlmZddlmZddlmZddlmZddlmZddlmZddlmZddlm Z ddlm!Z!ddlm"Z"ddlm#Z#ddlm$Z$dd lm%Z%dd!lm&Z&dd"lm'Z'dd#lm(Z(dd$lm)Z)dd%lm*Z*dd&lm+Z+dd'lm,Z,dd(lm-Z-dd)lm.Z.dd*lm/Z/dd+lm0Z0dd,lm1Z1dd-lm2Z2dd.lm3Z3dd/lm4Z4dd0lm5Z5dd1lm6Z6dd2lm7Z7dd3lm8Z8dd4lm9Z9dd5lm:Z:dd6lm;Z;dd7lmZ>dd:lm?Z?dd;lm@Z@dlmBZBdZed?d!d@dAZd dBdCdDdEZdFdGdHdIdJZdKdLdMdNdOZdPdQdRdSdTZd dLdUdVdWZ ddXdYdZd[Z!d\d d d]d^d_d`Z"d\d d d]d^dadbZ#dqddd$d$dedfdgZ$dd dhdidjZ%dd!dkdlZ&dmd!dndoZ'dS)rQueryableAttributeaBase class for :term:`descriptor` objects that intercept attribute events on behalf of a :class:`.MapperProperty` object. The actual :class:`.MapperProperty` is accessible via the :attr:`.QueryableAttribute.property` attribute. .. seealso:: :class:`.InstrumentedAttribute` :class:`.MapperProperty` :attr:`_orm.Mapper.all_orm_descriptors` :attr:`_orm.Mapper.attrs` ) class_keyimpl comparatorpropertyparent expression_of_type_extra_criteriaZ_slots_dispatchZ_propagate_attrs_docTz%dispatcher[QueryableAttribute[_T_co]]dispatch_ExternalEntityType[Any]rjstrrk_InternalEntityType[Any] parententity AttributeImplrlz interfaces.PropComparator[_T_co]rmz"Optional[_InternalEntityType[Any]]rqzTuple[ColumnElement[bool], ...]rr Optional[str]rsZorm_instrumented_attributeN_ExternalEntityType[_O]_InternalEntityType[_O]zOptional[AttributeImpl])rjrkrxrmrlof_typeextra_criteriac Cs||_||_||_|_||_|dk s*t||_||_||_d|_ t |}|r|j D]2} || krT|j | |j | |j jrTd|j _qTdSNT)rjrk _parententityrorlAssertionErrorrmrqrrrsr.Z_basesrt_update_active_history) selfrjrkrxrmrlr~rmanagerrBr{r{I/opt/hc_python/lib64/python3.8/site-packages/sqlalchemy/orm/attributes.py__init__s     zQueryableAttribute.__init__rrreturncCst|j|jjj|j|jjffSN)_queryable_attribute_unreducerkrZmapperrjentityrr{r{r __reduce__szQueryableAttribute.__reduce__boolcCs|jjSr)rl uses_objectsrr{r{r_impl_uses_objectssz%QueryableAttribute._impl_uses_objectsr7History)instancepassivercCs|jt|t||Sr)rl get_historyr!r)rrrr{r{rrs zQueryableAttribute.get_historyr]cCs|jjS)aReturn the 'info' dictionary for the underlying SQL element. The behavior here is as follows: * If the attribute is a column-mapped property, i.e. :class:`.ColumnProperty`, which is mapped directly to a schema-level :class:`_schema.Column` object, this attribute will return the :attr:`.SchemaItem.info` dictionary associated with the core-level :class:`_schema.Column` object. * If the attribute is a :class:`.ColumnProperty` but is mapped to any other kind of SQL expression other than a :class:`_schema.Column`, the attribute will refer to the :attr:`.MapperProperty.info` dictionary associated directly with the :class:`.ColumnProperty`, assuming the SQL expression itself does not have its own ``.info`` attribute (which should be the case, unless a user-defined SQL construct has defined one). * If the attribute refers to any other kind of :class:`.MapperProperty`, including :class:`.Relationship`, the attribute will refer to the :attr:`.MapperProperty.info` dictionary associated with that :class:`.MapperProperty`. * To access the :attr:`.MapperProperty.info` dictionary of the :class:`.MapperProperty` unconditionally, including for a :class:`.ColumnProperty` that's associated directly with a :class:`_schema.Column`, the attribute can be referred to using :attr:`.QueryableAttribute.property` attribute, as ``MyClass.someattribute.property.info``. .. seealso:: :attr:`.SchemaItem.info` :attr:`.MapperProperty.info` )rminforr{r{rr s(zQueryableAttribute.inforozColumnElement[_T_co]rpzColumnElement[_T]c Cs|j}t|tst|jtkr(d|i}n|j|j|d}|j}zt rVt|t sVt|j }Wn6t k r}zt d||f|W5d}~XYn X||SdS)Nentity_namespace) proxy_keyZ proxy_ownerrz}When interpreting attribute "%s" as a SQL expression, expected __clause_element__() to return a ClauseElement object, got: %r)_entity_namespace isinstancerGrrk_UNKNOWN_ATTR_KEYrrm__clause_element__rr`Z _annotateAttributeErrorrZInvalidRequestError)rrrZceannoZaer{r{r_memoized_attr_expressionGs,    z,QueryableAttribute._memoized_attr_expressionr^cCstd|jdS)NZorm)Zcompile_state_pluginZplugin_subject)r?Z immutabledictZ _parentmapperrr{r{r_memoized_attr__propagate_attrsis z2QueryableAttribute._memoized_attr__propagate_attrscCs|jSr)rrr{r{rrtsz$QueryableAttribute._entity_namespacer_cCs |jSr)r _annotationsrr{r{rrxszQueryableAttribute._annotationscCs|jSr)rprr{r{rr|sz%QueryableAttribute.__clause_element__zList[FromClause]cCs|jjSr)rp _from_objectsrr{r{rrsz QueryableAttribute._from_objectsz(Sequence[Tuple[_DMLColumnArgument, Any]]valuercCs |j|S)z'Return setter tuples for a bulk UPDATE.)rm_bulk_update_tuplesrrr{r{rrsz&QueryableAttribute._bulk_update_tupleszAliasedInsp[Any]rK)adapt_to_entityrcCs,|jr t|j|j|j|j|j||dS)N)rlrmrx)rqr __class__rrkrlrmrrrr{r{rrs  z"QueryableAttribute.adapt_to_entityz_EntityType[_T]zQueryableAttribute[_T])rrc Cs.t|j|j|j|j|j|t||j dSNrlrmr~r) rirjrkrrlrmr~r>inspectrr)rrr{r{rr~s zQueryableAttribute.of_typez_ColumnExpressionArgument[bool]zQueryableAttribute[bool])clausesrc Gs\trt|jtjsttddt|D}t |j |j |j |j |jj||j|j|dS)Ncss|]}ttj|VqdSr)rDexpectrEZWhereHavingRole).0Zclauser{r{r sz*QueryableAttribute.and_..r)rrrmrVZ Comparatorrtupler?Zcoerce_generator_argrirjrkrrland_rqrr)rrZexprsr{r{rrs zQueryableAttribute.and_)kwrc Ks$t|j|j|j|j|j|j|jdSr)rirjrkrrlrmrqrrrrr{r{r_cloneszQueryableAttribute._clonez Label[_T_co])namercCs||Sr)rlabel)rrr{r{rrszQueryableAttribute.labelrbzColumnElement[Any])opotherkwargsrcOs||jf||Srrmrrrrr{r{roperateszQueryableAttribute.operatecKs|||jf|Srrrr{r{rreverse_operatesz"QueryableAttribute.reverse_operateFrhstate optimisticrcCs|jj||ddk S)N)rFrl hasparent)rrrr{r{rrszQueryableAttribute.hasparent)rkrc Csztj||WStk r$YnXzt|j|WStk rz}z(tdt|jt|jj||f|W5d}~XYnXdS)NFNeither %r object nor %r object associated with %s has an attribute %rr? MemoizedSlots __getattr__rgetattrrmtype__name__)rrkerrr{r{rrs$  zQueryableAttribute.__getattr__cCs|jjd|jSN.rjrrkrr{r{r__str__szQueryableAttribute.__str__zOptional[MapperProperty[Any]]cCs|jjSrrmrnrr{r{r_memoized_attr_propertysz*QueryableAttribute._memoized_attr_property)NNr{)F)(r __module__ __qualname____doc__ __slots__Z is_attribute__annotations__Z__visit_name__rrFExtendedInternalTraversal dp_stringdp_multirIZdp_clauseelement_list_cache_key_traversalrrnrr4rrrrrrrrrrr~rrrrrrrrrr{r{r{rrisj '     )  "    rirvzType[_O]r}rur)rk mapped_classrxrrcCs$t|r||||St||SdSr)rZ_get_from_serializedr)rkrrxrr{r{rrsrcseZdZdZdZdZejddddZejddd d dZej ddfd d Zd d ddddZ d ddddZ e dd ddddZ e d d ddddZ dd ddddZ ZS)InstrumentedAttributezClass bound instrumented attribute which adds basic :term:`descriptor` methods. See :class:`.QueryableAttribute` for a description of most features. r{TrzrcCs|jSrrsrr{r{rr szInstrumentedAttribute.__doc__NonercCs ||_dSrrrr{r{rrscstjSr)superr)clsrr{rrsobjectr)rrrcCs|jt|t||ddSr)rlsetr!r)rrrr{r{r__set__s zInstrumentedAttribute.__set__rrcCs|jt|t|dSr)rldeleter!r)rrr{r{r __delete__sz InstrumentedAttribute.__delete__zInstrumentedAttribute[_T_co])rownerrcCsdSrr{rrrr{r{r__get__ szInstrumentedAttribute.__get__recCsdSrr{rr{r{rr%sOptional[object]z*Union[InstrumentedAttribute[_T_co], _T_co]c Cs~|dkr |St|}|jjr0|j|kr0||jSz t|}Wn.tk rj}zt||W5d}~XYnX|j||SdSr) rrlsupports_populationrkr!rorm_excZUnmappedInstanceErrorget)rrrdict_rrr{r{rr(s  )rrrrrZ inherit_cacher?Zrw_hybridpropertysetterZ classlevelrrr r __classcell__r{r{rrrs r)frozenc@sReZdZUdejfgZded<dZded<dZded<dZ ded <e d d Z d S) AdHocHasEntityNamespacerz ClassVar[_TraverseInternalsType]_traverse_internals)rrwFzClassVar[bool] is_mapperis_aliased_classcCs|jjSr)rrrr{r{rrEsz(AdHocHasEntityNamespace.entity_namespaceN) rrrrIZdp_has_cache_keyrrrrrrnrr{r{r{rr9s    rz&Callable[..., QueryableAttribute[Any]]) descriptorrcsBGfdddtt}tjd|_tj|tdd|S)zCreate an QueryableAttribute / user descriptor hybrid. Returns a new QueryableAttribute type that delegates descriptor behavior and getattr() to the given descriptor. cseZdZdZdZd!ddZeddZedd Zd Z d e j j fd e j j fgZed dZeddZeddZejddZddZddZddZddddZfdd ZdS)"z'create_proxied_attribute..ProxyzPresents the :class:`.QueryableAttribute` interface as a proxy on top of a Python descriptor / :class:`.PropComparator` combination. r{NcSs4||_||_||_||_||_||_||_|_dSr)rjrkroriginal_property _comparator_adapt_to_entityrsr)rrjrkrrmrdocrr{r{rrcs z0create_proxied_attribute..Proxy.__init__cSstj|jddSNF)Zraiseerrr>rrjrr{r{rrusz5create_proxied_attribute..Proxy._parententitycSstj|jddSrrrr{r{rroysz.create_proxied_attribute..Proxy.parentTrkrcSs|jdk ot|j|jjjSr)rrrjrkrlrrr{r{rrs z:create_proxied_attribute..Proxy._impl_uses_objectscSs"t|jdr|jjSt|jSdS)Nr)hasattrrrrrr{r{rrs z9create_proxied_attribute..Proxy._entity_namespacecSs|jjSrrrr{r{rrnsz0create_proxied_attribute..Proxy.propertycSs0t|jr||_|jr*|j|j|_|jSr)callablerrrrr{r{rrms  z2create_proxied_attribute..Proxy.comparatorcSs||j|j|j|j|Sr)rrrkrrrr{r{rrsz7create_proxied_attribute..Proxy.adapt_to_entityc[s"|j|j|j|j|j|j|jdS)N)rr)rrjrkrrrrrr{r{rrsz.create_proxied_attribute..Proxy._clonecSs,|j||}||jkr$|dkr$|S|SdSr)rr)rrrretvalr{r{rrsz/create_proxied_attribute..Proxy.__get__rvrcSs|jjd|jSrrrr{r{rrsz/create_proxied_attribute..Proxy.__str__c sztj||WStk r$YnXz t|WStk r }z|dkrXtd|z |j}Wn<tk r}ztdtj||f|W5d}~XYn^Xzt||WWYLStk r}z&tdtjt|j||f|W5d}~XYnXW5d}~XYnXdS)zNDelegate __getattr__ to the original descriptor and/or comparator.rmz[Neither %r object nor unconfigured comparator object associated with %s has an attribute %rNrr)r attributerrmZerr2Zerr3rr{rrsB    z3create_proxied_attribute..Proxy.__getattr__)NNN)rrrrrrrrnrroZ_is_internal_proxyrFrrrrrrr?Zmemoized_propertyrmrrrrrr{rr{rProxyVs6             rr)rZ from_instance)rirrrr?Zmonkeypatch_proxied_specials)rrr{rrcreate_proxied_attributeJs rZREMOVEAPPENDZREPLACEZ BULK_REPLACEZMODIFIEDc@s@eZdZdZdZdddddZdd Zed d Zd d Z dS)AttributeEventTokenaA token propagated throughout the course of a chain of attribute events. Serves as an indicator of the source of the event and also provides a means of controlling propagation across a chain of attribute operations. The :class:`.Event` object is sent as the ``initiator`` argument when dealing with events such as :meth:`.AttributeEvents.append`, :meth:`.AttributeEvents.set`, and :meth:`.AttributeEvents.remove`. The :class:`.Event` object is currently interpreted by the backref event handlers, and is used to control the propagation of operations across two mutually-dependent attributes. .. versionchanged:: 2.0 Changed the name from ``AttributeEvent`` to ``AttributeEventToken``. :attribute impl: The :class:`.AttributeImpl` which is the current event initiator. :attribute op: The symbol :attr:`.OP_APPEND`, :attr:`.OP_REMOVE`, :attr:`.OP_REPLACE`, or :attr:`.OP_BULK_REPLACE`, indicating the source operation. rlr parent_tokenryz util.symbol)attribute_implrcCs||_||_|jj|_dSrr)rr rr{r{rrszAttributeEventToken.__init__cCs"t|to |j|jko |j|jkSr)rrrlr)rrr{r{r__eq__s    zAttributeEventToken.__eq__cCs|jjSr)rlrkrr{r{rrk&szAttributeEventToken.keycCs |j|Srr)rrr{r{rr*szAttributeEventToken.hasparentN) rrrrrrr rnrkrr{r{r{rrs rc@seZdZUdZded<ded<ded<ded<ded<dZd ed <d ed <d ed <dKddddddddddddd ddZdZddddZddZ d d!Z e e e Z dLd"ddd#d$d%Z d"d"dd&d'd(d)Zefd"d*d+d,d-d.d/Zefd"d*d+d0d-d1d2Zd"d*dd3d4d5Zefd"d*d+dd-d6d7Zd"dd+dd8d9d:Zefd"d*ddd+d&d;dd?Zefd"d*ddd+d&d;d@dAZd ed dfd"d*ddd+ddd&dBdCdDZd"d*d&d3dEdFZefd"d*d+dd-dGdHZdIdJZd S)Mryz4internal implementation for instrumented attributes.r collectiondefault_accepts_scalar_loaderrrdynamicFr_replace_token _remove_token _append_tokenNTr|rvOptional[_LoaderCallable]"_Dispatch[QueryableAttribute[Any]]zOptional[Callable[..., bool]]Optional[AttributeEventToken]zOptional[bool]r) rjrk callable_rt trackparentcompare_functionactive_historyr load_on_unexpiresend_modified_eventsaccepts_scalar_loaderrc Ks||_||_||_||_||_|p$||_| |_|dkr@tj|_ n||_ | dk rV| |_ n|j |_ | dd} | |_ |r|d|j_| |_t|t|_dS)aConstruct an AttributeImpl. :param \class_: associated class :param key: string name of the attribute :param \callable_: optional function which generates a callable based on a parent instance, which produces the "default" values for a scalar or collection attribute when it's first accessed, if not present already. :param trackparent: if True, attempt to track if an instance has a parent attached to it via this attribute. :param compare_function: a function that compares two values which are normally assignable to this attribute. :param active_history: indicates that get_history() should always return the "old" value, even if it means executing a lazy callable upon attribute change. :param parent_token: Usually references the MapperProperty, used as a key for the hasparent() function to identify an "owning" attribute. Allows multiple AttributeImpls to all match a single owner attribute. :param load_on_unexpire: if False, don't include this attribute in a load-on-expired operation, i.e. the "expired_attribute_loader" process. The attribute can still be in the "expired" list and be considered to be "expired". Previously, this flag was called "expire_missing" and is only used by a deferred column attribute. :param send_modified_events: if False, the InstanceState._modified_event method will have no effect; this means the attribute will never show up as changed in a history entry. N_deferred_historyFT)rjrkrrtrr roperatoreqis_equalrr poprrrr OP_MODIFIED_modified_token)rrjrkrrtrrrr rrrrrr{r{rrAs&;   zAttributeImpl.__init__) rjrkrrtrr rrrr"rrrcCs|jjd|jSrrrr{r{rrszAttributeImpl.__str__cCs|jjS)z(Backwards compat for impl.active_historyrtrrr{r{r_get_active_historysz!AttributeImpl._get_active_historycCs ||j_dSrr#rr{r{r_set_active_historysz!AttributeImpl._set_active_historyrhrcCs*d}|jst||jt|j|dk S)a4Return the boolean value of a `hasparent` flag attached to the given state. The `optimistic` flag determines what the default return value should be if no `hasparent` flag can be located. As this function is used to determine if an instance is an *orphan*, instances that were loaded from storage should be assumed to not be orphans, until a True/False value for this flag is set. An instance attribute that is loaded by a callable function will also not have a `hasparent` flag. 6This AttributeImpl is not configured to track parents.F)rrparentsridr )rrrmsgr{r{rrszAttributeImpl.hasparentr)r parent_staterrcCsd}|jst|t|j}|r,||j|<n`||jkr|j|}|dk r|j|jkr|dkr~tdt |t ||jfdSd|j|<dS)zSet a boolean flag on the given item corresponding to whether or not it is attached to a parent object via the attribute represented by this ``InstrumentedAttribute``. r&FNzRemoving state %s from parent state %s along attribute '%s', but the parent record has gone stale, can't be sure this is the most recent parent.) rrr(r r'rkobjrZStaleDataErrorr;)rrr*rr)Zid_Z last_parentr{r{r sethasparents,        zAttributeImpl.sethasparentrOr7rrrrrcCs tdSrNotImplementedErrorrrrrr{r{rrszAttributeImpl.get_history_AllPendingTypecCs tdS)aReturn a list of tuples of (state, obj) for all objects in this attribute's current state + history. Only applies to object-based attributes. This is an inlining of existing functionality which roughly corresponds to: get_state_history( state, key, passive=PASSIVE_NO_INITIALIZE).sum() Nr.r0r{r{rget_all_pendingszAttributeImpl.get_all_pendingrrrcCs@|j|kstdd}|jjD]}||||}|tk r|}q|S)z=Produce an empty value for an uninitialized scalar attribute.O_default_value should only be invoked for an uninitialized or expired attributeN)rkrrtZ init_scalarr)rrrrfnretr{r{r_default_values   zAttributeImpl._default_valuec Cs|j|kr||jS|j}||jks2|j|tkr|t@s>tS||||}|tks\|tkr`|S|tkrz ||WStk r}ztd||W5d}~XYqXn|tk r| |||S|t @stS| ||SdS)zRetrieve a value from the given object. If a callable is assembled on this object's attribute, and passive is False, the callable will be executed and the resulting value will be set as the new value for this attribute. z=Deferred loader for attribute %r failed to populate correctlyN) rkcommitted_stater,rr3_fire_loader_callablesrKeyErrorrset_committed_valuerr7)rrrrrkrrr{r{rr's8    zAttributeImpl.getrrkrrcCsZ|jr"|jr"||jkr"|||S||jkr@|j|}|||S|jrR|||StSdSr)rrexpired_attributesZ _load_expiredZ callablesrr)rrrkrrr{r{rr9Ts     z$AttributeImpl._fire_loader_callablesrrr initiatorrrcCs|j|||||ddSNrrrrrrr?rr{r{rappendeszAttributeImpl.appendcCs|j||d|||ddS)N)r check_oldrBrCr{r{rremoveoszAttributeImpl.removec Cs|j||d|||dddS)NT)rrEr rBrCr{r{rr {szAttributeImpl.poprrrr?rrEr rcCs tdSrr.)rrrrr?rrEr r{r{rrs zAttributeImpl.setcCs tdSrr.)rrrr{r{rrszAttributeImpl.deletecCs>|j|jkr*|j|j}|tkr$dS|Sn|j|||dSdS)z,return the unchanged value of this attributeNrA)rkr8r,r)rrrrrr{r{rget_committed_values   z!AttributeImpl.get_committed_valuecCs|||j<|||jg|S)z=set an attribute value on the given instance and 'commit' it.)rk_commit)rrrrr{r{rr;s z!AttributeImpl.set_committed_value)FNFNTTN)F)rrrrr_is_has_collection_adapterrrrr$r%rnrrr,r4rr2r2r7rr9rDrFr rrrHr;r{r{r{rry2sb &U / - ryc seZdZdZdZdZdZdZdZdZ fddZ ddd d d d Z e fdd dddddZ de ddfdd dddddd dddZdddddddddZddddd dd d!ZZS)"ScalarAttributeImplz8represents a scalar value-holding InstrumentedAttribute.TF)rrrcs0tj||t|t|_|_t|t|_dSr)rrr OP_REPLACErr OP_REMOVEr)rargrrr{rrs  zScalarAttributeImpl.__init__rhrOrr3cCs|jjr|||t}n||jt}|jjr@|||||j| |||| |jt}|tkr|tkr|j s|j|j krt d|dS)N%s object does not have a value)rtrrr6rkr,rFfire_remove_eventr_modified_eventr Zexpiredr=rrrroldexistingr{r{rrs  zScalarAttributeImpl.deletezDict[str, Any]r7rr-cCsv|j|krt||||jS|j|jkr8t||tS|t@rH|tN}|j|||d}|tkrdtSt|||SdSr@) rkrfrom_scalar_attributer8r,rrr3 HISTORY_BLANK)rrrrcurrentr{r{rrs  zScalarAttributeImpl.get_historyNrrrrrGc Cs\|jjr|||t}n||jt}|jjr@||||||}|||||||j<dSr) rtrrr6rkr,rfire_replace_eventrQ rrrrr?rrEr rSr{r{rrs zScalarAttributeImpl.setrdrrrpreviousr?rcCs&|jjD]}|||||p|j}q|Sr)rtrrrrrrr[r?r5r{r{rrXs z&ScalarAttributeImpl.fire_replace_eventrrrr?rcCs$|jjD]}||||p|jqdSr)rtrFrrrrrr?r5r{r{rrPs z%ScalarAttributeImpl.fire_remove_event)rrrrr rrr rrrrr4rrrXrPrr{r{rrrKs$ rKc @seZdZdZdZdZdZdZdZddddd d Z e fddd d d ddZ e fddd dd ddZ de ddfddddd ddddddZddddddddZddddddddd ZdS)!ScalarObjectAttributeImplzrepresents a scalar-holding InstrumentedAttribute, where the target object is also instrumented. Adds events to delete/set operations. FTr{rhrOrr3cCs|jjr"|j||ttBtBd}n|j||ttAtBtBd}| ||||j | |j t }|t kr|tk r|j dkrtd|dS)NrArO)rtrrr5r(r#r0rr+rPrr rkr,r3rrRr{r{rr+s8z ScalarObjectAttributeImpl.deleter7rr-cCs|j|kr||j}n,|t@r&|tN}|j|||d}|tkrBtS|jsVt|||S|j|jt }|tkr|t t Bt Bt BtBB}|||j|}tj||||dSdS)NrA)original)rkrrr3rVrrfrom_object_attributer8 _NO_HISTORYr5r(r#r+rr9)rrrrrWr`Zloader_passiver{r{rrKsB  z%ScalarObjectAttributeImpl.get_historyr1cCs|j|kr||j}n|t@r0|j|||d}ngS|dk r\|tk r\|tk r\t||fg}ndg}|j|jkr|j|j}|dk r|tk r|tk r||k r|t||f|S)NrA)NN)rkrrr3r,r!r8rD)rrrrrWr6r`r{r{rr2ms2    z)ScalarObjectAttributeImpl.get_all_pendingNrrrrGc Cs|jjr"|j||ttBtBd}n|j||ttAtBtBd}|dk rz|t k rz||k rz|r^dSt dt |t ||j f||||||}|||j <dS)z'Set a value on the given InstanceState.rANz2Object %s not associated with %s on attribute '%s')rtrrr5r(r#r0rr+r3 ValueErrorr"r;rkrXrYr{r{rrsD  zScalarObjectAttributeImpl.setr]cCsX|jr&|dttfkr&|t||d|jjD]}||||p@|jq.||||dS)NF) rr3r,r,r!rtrFrrQr^r{r{rrPs z+ScalarObjectAttributeImpl.fire_remove_eventrdrZcCs|jr.||k r.|dttfkr.|t||d|jjD]}|||||pJ|j}q6|||||jr~|dk r~|t||d|S)NFT) rr3r,r,r!rtrrrQr\r{r{rrXs&  z,ScalarObjectAttributeImpl.fire_replace_event)rrrrr rrr rrr4rr2r2rrPrXr{r{r{rr_s$$&)-r_c @seZdZUdZded<dZdddddd d d Zed dd ddddddZed!dd ddddddZed"dd ddddddZde j fdd ddddddZde j dddfdd dddddddd ddZ dS)#HasCollectionAdapterr{rr TrhrSrTrrr adapter fire_eventrcCs tdSrr.rrr rfrgr{r{r_dispose_previous_collectionsz1HasCollectionAdapter._dispose_previous_collection.rO Literal[None] Literal[PassiveFlag.PASSIVE_OFF]rr user_datarrcCsdSrr{rrrrmrr{r{rget_collectionsz#HasCollectionAdapter.get_collectionr7cCsdSrr{rnr{r{rros$Optional[_AdaptedCollectionProtocol]IUnion[Literal[LoaderCallableStatus.PASSIVE_NO_RESULT], CollectionAdapter]cCsdSrr{rnr{r{rros NcCs tdSrr.rnr{r{rros Frr rrrr?rrEr _adaptrc Cs tdSrr.) rrrrr?rrEr rsr{r{rr&s zHasCollectionAdapter.set)..)..)..) rrrrrrJrir ror7r4rr{r{r{rrds2  rdz"TypeGuard[CollectionAttributeImpl])rlrcCsdSrr{)rlr{r{r_is_collection_attribute_impl6srtr c s<eZdZUdZdZdZdZdZdZde d<dZ dOfdd Z d d Z e fd d dddddZefd d dddddZd d dddddddZd d dddddddZd d ddddd d!Zd d d"ddddd#d$Zd d dd%d&d'Zd d d(d%d)d*Zd d+d,d-d.Ze fd d d"dddd/d0d1Ze fd d d"dddd/d2d3Ze fd d d"dddd/d4d5Zdej dddfd d d"ddd"d6d6dd7 d8d9Zd d(d:d6dd;dd?d@Zd d d"d(dAdBdCZe dPd d dEdFd:dGdHdIZ!e dQd d d(dd:dGdJdIZ!e dDe fd d dKddLdGdMdIZ!de fd d dKddLdGdNdIZ!Z"S)RCollectionAttributeImplaA collection-holding attribute that instruments changes in membership. Only handles collections of instrumented objects. InstrumentedCollectionAttribute holds an arbitrary, user-specified container object (defaulting to a list) and brokers access to the CollectionAdapter, a "view" onto that object that presents consistent bag semantics to the orm layer independent of the user data implementation. TFr_bulk_replace_token)copycollection_factoryrrrv_duck_typed_asNc  stj||||f||d| |dkr.|j}||_||_t|t|_t|t|_ t|t |_ t ||_t|jddrt|ddd} t|ddd} dS) N)rr _sa_linkerinit_collectioncSs||dSrrztargetr collection_adapterr{r{rlink~sz.CollectionAttributeImpl.__init__..linkdispose_collectioncSs|ddSrr|r}r{r{runlinksz0CollectionAttributeImpl.__init__..unlink)rr_CollectionAttributeImpl__copyrwrxr OP_APPENDrrMrOP_BULK_REPLACErvr?duck_type_collectionryrr=Z listens_for) rrjrkrrt typecallabler copy_functionrrrrrr{rr[s4        z CollectionAttributeImpl.__init__cCsddt|DS)NcSsg|]}|qSr{r{)ryr{r{r sz2CollectionAttributeImpl.__copy..)rr)ritemr{r{rZ__copyszCollectionAttributeImpl.__copyrhrOr7rr-cCsv|j|||d}|tkrJ|tj@rD|j|jkrD|j|j}|tStSn(|tj@rd|j|jksdtt |||SdSr@) rr3r7rrk_pending_mutationsZmerge_with_historyrVrrfrom_collection)rrrrrWpendingr{r{rrs    z#CollectionAttributeImpl.get_historyr1cs|j|krgS||j}t|d}|j|jkr|j|j}|tk rdd|D}dd|D}t|t|fdd|Dfdd|Dfdd|DSdd|DS) N _sa_adaptercSs$g|]}|dk rt|pd|fqSrr rcr{r{rrsz;CollectionAttributeImpl.get_all_pending..cSs$g|]}|dk rt|pd|fqSrr rr{r{rrscs g|]\}}|kr||fqSr{r{rso original_setr{rrscs g|]\}}|kr||fqSr{r{rrr{rrscs g|]\}}|kr||fqSr{r{r current_setr{rrscSsg|]}t||fqSr{r )rrr{r{rrs)rkrr8r,dict)rrrrrWr`current_statesoriginal_statesr{rrrr2s4       z'CollectionAttributeImpl.get_all_pendingrdrz Optional[Any])rrrr?rkrcCsX|jjD]}||||p|j|d}q|||td|jrT|dk rT|t||d|S)NrkT)rtrDrrQr,rr,r!rrrrr?rkr5r{r{rfire_append_events  z)CollectionAttributeImpl.fire_append_eventcCs(|jjD]}||||p|j|d}q|S)Nr)rtZappend_wo_mutationrrr{r{rfire_append_wo_mutation_events z5CollectionAttributeImpl.fire_append_wo_mutation_eventr)rrr?rkrcCs|||tddS)afA special event used for pop() operations. The "remove" event needs to have the item to be removed passed to it, which in the case of pop from a set, we don't have a way to access the item before the operation. the event is used for all pop() operations (even though set.pop is the one where it is really needed). TN)rQr,)rrrr?rkr{r{rfire_pre_remove_eventsz-CollectionAttributeImpl.fire_pre_remove_eventrcCsX|jr |dk r |t||d|jjD]}||||p:|j|dq(|||tddS)NFrT)rr,r!rtrFrrQr,rr{r{rrPs  z)CollectionAttributeImpl.fire_remove_eventr3cCs@|j|krdS|||td|||j}|||j=dSr)rkrQr,rorZclear_with_event)rrrr r{r{rrs  zCollectionAttributeImpl.deleterScCsF|j|kstd|j|jkr*|j|jS||\}}|||S)z;Produce an empty collection for an un-initialized attributer4)rkr_empty_collections_initialize_collectionZ _set_empty)rrrrfrmr{r{rr7s    z&CollectionAttributeImpl._default_valuez4Tuple[CollectionAdapter, _AdaptedCollectionProtocol])rrcCs0|j|j||j\}}|j|||||fSr)rZinitialize_collectionrkrxrtr{)rrrfr r{r{rr-sz.CollectionAttributeImpl._initialize_collectionr>cCsv|j||d|d}|tkrT|j||||td}|j|ks@td||j|ntrft |t sft| ||dSN)rmrrz,Collection was loaded during event handling.) ror3rr*rkr_get_pending_mutationrDrrrTZappend_with_eventrrrrr?rr r{r{rrD8s,zCollectionAttributeImpl.appendcCsx|j||jd|d}|tkrV|j||||td|j|ksBtd||j|nt rht |t sht| ||dSr) rorr3rPr*rkrrrFrrrTZremove_with_eventrr{r{rrFPs zCollectionAttributeImpl.removec Cs8z|j|||||dWntttfk r2YnXdSr@)rFrcr: IndexErrorrCr{r{rr fszCollectionAttributeImpl.poprrrc Csn|} } d} ||\} } |r| jdk r4| | } qt| }|j}||k rx| dkrXdp^| jj}|jj}td||ft| dr| } q|t krt | } | } qt | } nt| t krt |} t | }|j}|jj|||| d|j||t|tj@Ad}|tkr|||}n|| kr(dS||||d|j}| ||j<tj||| |d||||ddS)Nrz/Incompatible collection type: %s is not %s-like _sa_iterator)keysrAT)r?)rZ _converterr?rryrr TypeErrorrrrlistvaluesiterrvrtZ bulk_replacerr5r7r+r3r7rQrrkrri)rrrrr?rrEr rsiterableZ orig_iterableZnew_keysZnew_collectionrmZ setting_typeZreceiving_typegivenwanted new_valuesZevtrSold_collectionr{r{rrvsd            zCollectionAttributeImpl.setrTrecCs,|`|j|jd|r(|j|||dSr)rrr rkrtrrhr{r{rrisz4CollectionAttributeImpl._dispose_previous_collection)r rcCst|d}d|_dS)NrT)rZ invalidated)rr rfr{r{r_invalidate_collections z.CollectionAttributeImpl._invalidate_collection)rrrrc Cs||\}}|r||||j|j<|||jg|j|jkr||||d|j|j}|j}|j }|D]} | | qr|D]} | | q|S)z=Set an attribute value on the given instance and 'commit' it.T) rZappend_multiple_without_eventrrkrIrrQr Z added_itemsZ deleted_itemsZappend_without_eventZremove_without_event) rrrrr rmraddedZremovedrr{r{rr;s     z+CollectionAttributeImpl.set_committed_value.rjrkrlcCsdSrr{rnr{r{rrosz&CollectionAttributeImpl.get_collectioncCsdSrr{rnr{r{rrosrprqcCsdSrr{rnr{r{rro s cCs6|dkr0|j|||d}|tjkr&|Std|}|jS)zRetrieve the CollectionAdapter associated with the given state. if user_data is None, retrieves it from the state using normal "get()" rules, which will fire lazy callables or return the "empty" collection value. NrArS)rr$r3rr)rrrrmrZfetch_user_datar{r{rros   )NFNN)..)..)#rrrrrr r rrrrrrr4rr2r2rrrrPrr7rrDrFr r7rrirr;r rorr{r{rrru>sn  +-   QruzQueryableAttribute[Any]rr)rrkuselistrcsjjjfddfdd}fdd}fdd}rhtjd |d d d d ntjd |d d d d tjd |d d d d dS)z6Apply listeners to synchronize a two-way relationship.cs$tdt||j|jjjfdS)NzBidirectional attribute conflict detected: Passing object %s to attribute "%s" triggers a modify event on attribute "%s" via the backref "%s".)rcr;r rl) child_stater? child_impl)rr{r_acceptable_key_err@sz.backref_listeners.._acceptable_key_errcs||kr |S|dk r~|tk r~|tk r~t|t|}}|jj}|jsV|jsV|j}n|j }||k r~|j ||| j t d|dk r t|t|} } | jj} |jk r|j| jk rȈ||| | j } t| r| jnd} || k r || k r | j| | | |t d|Sr@)r3r,r!rrrlr rrrr r+rr0r rtrvrD)rchildZoldchildr?r old_stateZold_dictrlZcheck_recursive_tokenr child_dictrcheck_append_tokencheck_bulk_replace_token)rrk parent_implr r{r"emit_backref_from_scalar_set_eventNsd      z=backref_listeners..emit_backref_from_scalar_set_eventc s|dkr dSt|t|}}|jj}|jk rL|j|jk rL||||j}t|r`|jnd}||k r||k r|j||| |t d|Sr@) r!rrrlr rrtrvrDr+r0) rrr?rrrrrr)rrkr r{r)emit_backref_from_collection_append_events4   zDbackref_listeners..emit_backref_from_collection_append_eventc s|dk r|tk r|tk rt|t|}}|jj}|js\|js\|j}|j }oXj } n|j}t |rp|j nd}d} ||k r||k r| rt |jj|s|j||||tddS)NFrA)r3r,r!rrrlr rrrrtrvr?Z has_dupesrrkr r+r0) rrr?rrrrZcheck_remove_tokenZcheck_replace_tokenZcheck_for_dupes_on_remove)rkrrr{r)emit_backref_from_collection_remove_eventsF   zDbackref_listeners..emit_backref_from_collection_remove_eventrDT)rrawZ include_keyrrFN)rlr r=listen)rrkrrrrr{)rrrkrr rrbackref_listeners0s>  C$0  rZ NO_HISTORYc@seZdZUdZded<ded<ded<dddd Zddd d Zd dd dZd dddZd dddZ ddddZ ddddddZ ddddZ e ddddddd Ze efd!ddddd"d#d$Ze d%ddddd&d'Zd(S))raA 3-tuple of added, unchanged and deleted values, representing the changes which have occurred on an instrumented attribute. The easiest way to get a :class:`.History` object for a particular attribute on an object is to use the :func:`_sa.inspect` function:: from sqlalchemy import inspect hist = inspect(myobject).attrs.myattribute.history Each tuple member is an iterable sequence: * ``added`` - the collection of items added to the attribute (the first tuple element). * ``unchanged`` - the collection of items that have not changed on the attribute (the second tuple element). * ``deleted`` - the collection of items that have been removed from the attribute (the third tuple element). zUnion[Tuple[()], List[Any]]r unchangeddeletedrrcCs|tkSr)rVrr{r{r__bool__# szHistory.__bool__cCst|jp|jp|j S)zhReturn True if this :class:`.History` has no changes and no existing, unchanged state. )rrrrrr{r{rempty& sz History.emptyz Sequence[Any]cCs|jpg|jpg|jpgS)z3Return a collection of added + unchanged + deleted.)rrrrr{r{rsum. sz History.sumcCs|jpg|jpgS)z)Return a collection of added + unchanged.)rrrr{r{r non_deleted5 szHistory.non_deletedcCs|jpg|jpgS)z+Return a collection of unchanged + deleted.)rrrr{r{r non_added: szHistory.non_addedcCst|jp |jS)z2Return True if this :class:`.History` has changes.)rrrrr{r{r has_changes? szHistory.has_changesz Iterable[Any])rrrcCs*tt|jt||jt|jt|Sr)rrrrr)rrrr{r{r_mergeD s zHistory._mergecCs0tdd|jDdd|jDdd|jDS)NcSs g|]}|dk rt|pdqSrr rr{r{rrM sz$History.as_state..cSs g|]}|dk rt|pdqSrr rr{r{rrQ scSs g|]}|dk rt|pdqSrr rr{r{rrU s)rrrrrr{r{ras_stateK szHistory.as_staterKrhr)rrrWrcCs|j|jt}|tkr<|tkr,|dddS|d|gdSnp|tk rb|||dkrb|d|gdSt|tkrd}t|tkrd}n|g}|tkr|dd|S||gd|SdS)Nr{T)r8rrkrbr,rr(_NO_STATE_SYMBOLSrrrrWr`rr{r{rrU[ s$    zHistory.from_scalar_attributer_)rrrWr`rcCs|tkr|j|jt}|tkrD|tkr4|dddS|d|gdSnp||krb|tk rb|d|gdSt|tksv|dkrd}t|tkrd}n|g}|tkr|dd|S||gd|SdS)Nr{)rbr8rrkr,r(rrr{r{rra s    zHistory.from_object_attributerucs|j|jt}|tkr$|dddSt|d}|tkrF|t|ddS|tkr^|dt|dSdd|D}dd|D}t|t||fdd|Dfdd|Dfdd|DSdS) Nr{rcSs$g|]}|dk rt|pd|fqSrr rr{r{rr sz+History.from_collection..cSs$g|]}|dk rt|pd|fqSrr rr{r{rr scsg|]\}}|kr|qSr{r{rrr{rr scsg|]\}}|kr|qSr{r{rrr{rr scsg|]\}}|kr|qSr{r{rrr{rr s)r8rrkrbr,rrr)rrrrWr`rrr{rrr s*  zHistory.from_collectionN)rrrrrrrrrrrrr classmethodrUrbrarr{r{r{rr s& (&rr{rr7)r+rkrrcCstt|||S)a Return a :class:`.History` record for the given object and attribute key. This is the **pre-flush** history for a given attribute, which is reset each time the :class:`.Session` flushes changes to the current database transaction. .. note:: Prefer to use the :attr:`.AttributeState.history` and :meth:`.AttributeState.load_history` accessors to retrieve the :class:`.History` for instance attributes. :param obj: an object whose class is instrumented by the attributes package. :param key: string attribute name. :param passive: indicates loading behavior for the attribute if the value is not already present. This is a bitflag attribute, which defaults to the symbol :attr:`.PASSIVE_OFF` indicating all necessary SQL should be emitted. .. seealso:: :attr:`.AttributeState.history` :meth:`.AttributeState.load_history` - retrieve history using loader callables if the value is not locally present. )get_state_historyr!)r+rkrr{r{rr s%rr<cCs |||Sr)r)rrkrr{r{rr srFrR)rr+rkrrcCst|}t|}||||S)TODO)r%r! has_parent)rr+rkrrrr{r{rr sr)rzinterfaces.PropComparator[_T]rzzInstrumentedAttribute[_T])rjrkrmrxrrrcKs$t|||||d}t||f||S)N)rmrxr)register_descriptorregister_attribute_impl)rjrkrmrxrrdescr{r{rregister_attribute s rrzOptional[Type[AttributeImpl]]) rjrkrr useobject impl_classbackrefrrc Kst|}|r*|dd} ||| p$t} n |dd} td||j} |rbtd|||| f|} nJ|rt|||| fd| i|} n*|rt|||| f|} nt|||| f|} | ||_ |rt ||||| |||S)NrrzType[WriteOnlyAttributeImpl]) r%r Zinstrument_collection_classrrrtrur_rKrlrZpost_configure_attribute) rjrkrrrrrrrfactoryrrtrlr{r{rr s\     rz Type[Any]rw)rjrkrmrxrrcCs.t|}t||||d}||_||||S)N)rmrx)r%rrZinstrument_attribute)rjrkrmrxrrrr{r{rrL s r)rjrkrcCst||dSr)r%Zuninstrument_attribute)rjrkr{r{runregister_attribute` srrT)r+rkrcCst|}|j}t|||S)a4Initialize a collection attribute and return the collection adapter. This function is used to provide direct access to collection internals for a previously unloaded attribute. e.g.:: collection_adapter = init_collection(someobject, 'elements') for elem in values: collection_adapter.append_without_event(elem) For an easier way to do the above, see :func:`~sqlalchemy.orm.attributes.set_committed_value`. :param obj: a mapped object :param key: string attribute name where the collection is located. )r!rinit_state_collection)r+rkrrr{r{rr{d sr{rO)rrrkrcCst|j|j}trt|tst||d}|dk rH|j}||||d| ||}|j |||t j d}| |S)zInitialize a collection attribute and return the collection adapter. Discards any existing collection which may be there. NFrA)rrlrrrdrr rrir7ror7r0Z _reset_empty)rrrkattrrSrrmrfr{r{rr{ s    rcCs,t|t|}}|j|j|||dS)a[Set the value of an attribute with no history events. Cancels any previous history present. The value should be a scalar value for scalar-holding attributes, or an iterable for any collection-holding attribute. This is the same underlying method used when a lazy loader fires off and loads additional data from the database. In particular, this method can be used by application code which has loaded additional attributes or collections through separate queries, which can then be attached to an instance as though it were part of its original loaded state. N)r!rrrlr;)rrkrrrr{r{rr; sr;r)rrkrr?rcCs.t|t|}}|j|j||||dS)asSet the value of an attribute, firing history events. This function may be used regardless of instrumentation applied directly to the class, i.e. no descriptors are required. Custom attribute management schemes will need to make usage of this method to establish attribute state as understood by SQLAlchemy. :param instance: the object that will be modified :param key: string name of the attribute :param value: value to assign :param initiator: an instance of :class:`.Event` that would have been propagated from a previous event listener. This argument is used when the :func:`.set_attribute` function is being used within an existing event listening function where an :class:`.Event` object is being supplied; the object may be used to track the origin of the chain of events. .. versionadded:: 1.2.3 N)r!rrrlr)rrkrr?rrr{r{r set_attribute sr)rrkrcCs&t|t|}}|j|j||S)aZGet the value of an attribute, firing any callables required. This function may be used regardless of instrumentation applied directly to the class, i.e. no descriptors are required. Custom attribute management schemes will need to make usage of this method to make usage of attribute state as understood by SQLAlchemy. )r!rrrlrrrkrrr{r{r get_attribute s rcCs*t|t|}}|j|j||dS)aQDelete the value of an attribute, firing history events. This function may be used regardless of instrumentation applied directly to the class, i.e. no descriptors are required. Custom attribute management schemes will need to make usage of this method to establish attribute state as understood by SQLAlchemy. N)r!rrrlrrr{r{r del_attribute s rcCsDt|t|}}|j|j}|j||j|j||tdddS)aMark an attribute on an instance as 'modified'. This sets the 'modified' flag on the instance and establishes an unconditional change event for the given attribute. The attribute must have a value present, else an :class:`.InvalidRequestError` is raised. To mark an object "dirty" without referring to any specific attribute so that it is considered within a flush, use the :func:`.attributes.flag_dirty` call. .. seealso:: :func:`.attributes.flag_dirty` TZ is_userlandN) r!rrrlrtmodifiedr"rQr,)rrkrrrlr{r{r flag_modified s rrcCs(t|t|}}|j|dtdddS)aMark an instance as 'dirty' without any specific attribute mentioned. This is a special operation that will allow the object to travel through the flush process for interception by events such as :meth:`.SessionEvents.before_flush`. Note that no SQL will be emitted in the flush process for an object that has no changes, even if marked dirty via this method. However, a :meth:`.SessionEvents.before_flush` handler will be able to see the object in the :attr:`.Session.dirty` collection and may establish changes on it, which will then be included in the SQL emitted. .. versionadded:: 1.2 .. seealso:: :func:`.attributes.flag_modified` NTr)r!rrQr,)rrrr{r{r flag_dirty sr)F)FNFNN)N)r __future__r dataclassesrtypingrrrrrrr r r r r rrrrrrrrrZ_typingrrBrrrrrrrrr!r"r#r$r%r&r'r(r)r*r+r,r-r.r/r0r1r2r3r4r5r6r7r8r9r:r;r=r>r?r@rAsqlZsql_baserCrDrErFZ sql.cache_keyrGZ sql.visitorsrHrIZ util.typingrJrKrLrMrNrOrPrQrRrSrTrUZ relationshipsrVrrWrXZ writeonlyrYZ event.baserZZ sql._typingr[r\r]r^Zsql.annotationr_Z sql.elementsr`raZ sql.operatorsrbZsql.selectablercrdrerr1rZ_self_inspectsZInspectionAttrZPropComparatorZJoinTargetRoleZ OnClauseRoleZ ImmutableZSlotsMemoizedHasCacheKeyrrirr dataclassrrsymbolrMrrLrr!rZAttributeEventEventryrKr_rdrt attrgetterrurrb frozensetr(rrrVrrrrrrrr{rr;rrrrrr{r{r{r sl                                                                                                 g? +     3gTE uS J ) 9"