o
    Åîï]#Œ  ã                   @   s†  d Z ddlZddlZddlZddlZddlmZ ddlmZ ddl	m
Z
mZmZ ddlmZ ddlmZ ddlmZ dd	lmZmZmZmZmZmZmZ dd
lmZ ddlmZ ddlm Z m!Z! ddl"m#Z# G dd„ de$ƒZ%G dd„ de$ƒZ&dd„ Z'G dd„ de$ƒZ(G dd„ de$ƒZ)G dd„ de$ƒZ*dd„ Z+dd„ Z,ee-ddgƒB Z.d Z/d!d"„ Z0G d#d$„ d$e$ƒZ1G d%d&„ d&e$ƒZ2G d'd(„ d(ej3ƒZ4dS ))aÌ  Logical sessions for ordering sequential operations.

Requires MongoDB 3.6.

.. versionadded:: 3.6

Causally Consistent Reads
=========================

.. code-block:: python

  with client.start_session(causal_consistency=True) as session:
      collection = client.db.collection
      collection.update_one({'_id': 1}, {'$set': {'x': 10}}, session=session)
      secondary_c = collection.with_options(
          read_preference=ReadPreference.SECONDARY)

      # A secondary read waits for replication of the write.
      secondary_c.find_one({'_id': 1}, session=session)

If `causal_consistency` is True (the default), read operations that use
the session are causally after previous read and write operations. Using a
causally consistent session, an application can read its own writes and is
guaranteed monotonic reads, even when reading from replica set secondaries.

.. mongodoc:: causal-consistency

.. _transactions-ref:

Transactions
============

MongoDB 4.0 adds support for transactions on replica set primaries. A
transaction is associated with a :class:`ClientSession`. To start a transaction
on a session, use :meth:`ClientSession.start_transaction` in a with-statement.
Then, execute an operation within the transaction by passing the session to the
operation:

.. code-block:: python

  orders = client.db.orders
  inventory = client.db.inventory
  with client.start_session() as session:
      with session.start_transaction():
          orders.insert_one({"sku": "abc123", "qty": 100}, session=session)
          inventory.update_one({"sku": "abc123", "qty": {"$gte": 100}},
                               {"$inc": {"qty": -100}}, session=session)

Upon normal completion of ``with session.start_transaction()`` block, the
transaction automatically calls :meth:`ClientSession.commit_transaction`.
If the block exits with an exception, the transaction automatically calls
:meth:`ClientSession.abort_transaction`.

For multi-document transactions, you can only specify read/write (CRUD)
operations on existing collections. For example, a multi-document transaction
cannot include a create or drop collection/index operations, including an
insert operation that would result in the creation of a new collection.

A session may only have a single active transaction at a time, multiple
transactions on the same session can be executed in sequence.

.. versionadded:: 3.7

Sharded Transactions
^^^^^^^^^^^^^^^^^^^^

PyMongo 3.9 adds support for transactions on sharded clusters running MongoDB
4.2. Sharded transactions have the same API as replica set transactions.
When running a transaction against a sharded cluster, the session is
pinned to the mongos server selected for the first operation in the
transaction. All subsequent operations that are part of the same transaction
are routed to the same mongos server. When the transaction is completed, by
running either commitTransaction or abortTransaction, the session is unpinned.

.. versionadded:: 3.9

.. mongodoc:: transactions

Classes
=======
é    N)ÚBinary)ÚInt64)ÚabcÚinteger_typesÚreraise_instance)ÚSON)Ú	Timestamp)Ú	monotonic)ÚConfigurationErrorÚConnectionFailureÚInvalidOperationÚOperationFailureÚPyMongoErrorÚServerSelectionTimeoutErrorÚWTimeoutError)Ú_RETRYABLE_ERROR_CODES)ÚReadConcern)ÚReadPreferenceÚ_ServerMode)ÚWriteConcernc                   @   s6   e Zd ZdZ		d
dd„Zedd„ ƒZedd	„ ƒZdS )ÚSessionOptionsaK  Options for a new :class:`ClientSession`.

    :Parameters:
      - `causal_consistency` (optional): If True (the default), read
        operations are causally ordered within the session.
      - `default_transaction_options` (optional): The default
        TransactionOptions to use for transactions started on this session.
    TNc                 C   s0   || _ |d urt|tƒstd|f ƒ‚|| _d S )Nzedefault_transaction_options must be an instance of pymongo.client_session.TransactionOptions, not: %r)Ú_causal_consistencyÚ
isinstanceÚTransactionOptionsÚ	TypeErrorÚ_default_transaction_options)ÚselfÚcausal_consistencyÚdefault_transaction_options© r   úH/var/www/html/env/lib/python3.10/site-packages/pymongo/client_session.pyÚ__init__ƒ   s   
þÿ
zSessionOptions.__init__c                 C   ó   | j S )z)Whether causal consistency is configured.)r   ©r   r   r   r    r   �   ó   z!SessionOptions.causal_consistencyc                 C   r"   )zThe default TransactionOptions to use for transactions started on
        this session.

        .. versionadded:: 3.7
        )r   r#   r   r   r    r   ”   s   z*SessionOptions.default_transaction_options)TN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r!   Úpropertyr   r   r   r   r   r    r   z   s    	
þ
r   c                   @   sN   e Zd ZdZ		ddd„Zedd„ ƒZedd„ ƒZed	d
„ ƒZedd„ ƒZ	dS )r   aÙ  Options for :meth:`ClientSession.start_transaction`.
    
    :Parameters:
      - `read_concern` (optional): The
        :class:`~pymongo.read_concern.ReadConcern` to use for this transaction.
        If ``None`` (the default) the :attr:`read_preference` of
        the :class:`MongoClient` is used.
      - `write_concern` (optional): The
        :class:`~pymongo.write_concern.WriteConcern` to use for this
        transaction. If ``None`` (the default) the :attr:`read_preference` of
        the :class:`MongoClient` is used.
      - `read_preference` (optional): The read preference to use. If
        ``None`` (the default) the :attr:`read_preference` of this
        :class:`MongoClient` is used. See :mod:`~pymongo.read_preferences`
        for options. Transactions which read must use
        :attr:`~pymongo.read_preferences.ReadPreference.PRIMARY`.
      - `max_commit_time_ms` (optional): The maximum amount of time to allow a
        single commitTransaction command to run. This option is an alias for
        maxTimeMS option on the commitTransaction command. If ``None`` (the
        default) maxTimeMS is not used.

    .. versionchanged:: 3.9
       Added the ``max_commit_time_ms`` option.

    .. versionadded:: 3.7
    Nc                 C   s®   || _ || _|| _|| _|d urt|tƒstd|f ƒ‚|d ur6t|tƒs,td|f ƒ‚|js6t	d|f ƒ‚|d urFt|t
ƒsFtd|f ƒ‚|d urSt|tƒsUtdƒ‚d S d S )NzMread_concern must be an instance of pymongo.read_concern.ReadConcern, not: %rzPwrite_concern must be an instance of pymongo.write_concern.WriteConcern, not: %rz<transactions do not support unacknowledged write concern: %rzT%r is not valid for read_preference. See pymongo.read_preferences for valid options.z-max_commit_time_ms must be an integer or None)Ú_read_concernÚ_write_concernÚ_read_preferenceÚ_max_commit_time_msr   r   r   r   Úacknowledgedr
   r   r   )r   Úread_concernÚwrite_concernÚread_preferenceÚmax_commit_time_msr   r   r    r!   ¹   s@   
þ
þÿÿ
þ
ÿþzTransactionOptions.__init__c                 C   r"   )z>This transaction's :class:`~pymongo.read_concern.ReadConcern`.)r*   r#   r   r   r    r/   ×   r$   zTransactionOptions.read_concernc                 C   r"   )z@This transaction's :class:`~pymongo.write_concern.WriteConcern`.)r+   r#   r   r   r    r0   Ü   r$   z TransactionOptions.write_concernc                 C   r"   )zNThis transaction's :class:`~pymongo.read_preferences.ReadPreference`.
        )r,   r#   r   r   r    r1   á   s   z"TransactionOptions.read_preferencec                 C   r"   )zfThe maxTimeMS to use when running a commitTransaction command.

        .. versionadded:: 3.9
        )r-   r#   r   r   r    r2   ç   s   z%TransactionOptions.max_commit_time_ms©NNNN)
r%   r&   r'   r(   r!   r)   r/   r0   r1   r2   r   r   r   r    r   ž   s    
ÿ


r   c                 C   s.   | r|dur|j s| jrdS td|f ƒ‚| S )z‚Validate that an explicit session is not used with an unack'ed write.

    Returns the session to use for the next operation.
    NzHExplicit sessions are incompatible with unacknowledged write concern: %r)r.   Ú	_implicitr
   )Úsessionr0   r   r   r    Ú_validate_session_write_concernð   s   ÿÿÿr6   c                   @   ó(   e Zd ZdZdd„ Zdd„ Zdd„ ZdS )	Ú_TransactionContextz;Internal transaction context manager for start_transaction.c                 C   s
   || _ d S ©N)Ú_TransactionContext__session)r   r5   r   r   r    r!     s   
z_TransactionContext.__init__c                 C   ó   | S r9   r   r#   r   r   r    Ú	__enter__  ó   z_TransactionContext.__enter__c                 C   s0   | j jr|d u r| j  ¡  d S | j  ¡  d S d S r9   )r:   Úin_transactionÚcommit_transactionÚabort_transaction©r   Úexc_typeÚexc_valÚexc_tbr   r   r    Ú__exit__  s
   üz_TransactionContext.__exit__N)r%   r&   r'   r(   r!   r<   rE   r   r   r   r    r8     s
    r8   c                   @   s$   e Zd ZdZdZdZdZdZdZdS )Ú	_TxnStateé   é   é   é   é   é   N)	r%   r&   r'   ÚNONEÚSTARTINGÚIN_PROGRESSÚ	COMMITTEDÚCOMMITTED_EMPTYÚABORTEDr   r   r   r    rF     s    rF   c                   @   r7   )	Ú_TransactionzBInternal class to hold transaction information in a ClientSession.c                 C   s$   || _ tj| _d| _d | _d | _d S ©NF)ÚoptsrF   rM   ÚstateÚshardedÚpinned_addressÚrecovery_token)r   rU   r   r   r    r!   !  s
   
z_Transaction.__init__c                 C   s   | j tjtjfv S r9   )rV   rF   rN   rO   r#   r   r   r    Úactive(  ó   z_Transaction.activec                 C   s   t j| _d| _d | _d | _d S rT   )rF   rM   rV   rW   rX   rY   r#   r   r   r    Úreset+  s   
z_Transaction.resetN)r%   r&   r'   r(   r!   rZ   r\   r   r   r   r    rS     s
    rS   c                 C   s"   |   d¡ t| t ¡ d d� dS )zDRe-raise an exception with the UnknownTransactionCommitResult label.ÚUnknownTransactionCommitResultrH   )ÚtraceN)Ú_add_error_labelr   ÚsysÚexc_info©Úexcr   r   r    Ú_reraise_with_unknown_commit2  s   
rd   c                 C   s   t | tƒo	| jdkS )z/Return true if exc is a MaxTimeMSExpired error.é2   )r   r   Úcoderb   r   r   r    Ú_max_time_expired_error8  s   rg   é@   re   éx   c                 C   s   t  ¡ |  tk S )z/Are we within the with_transaction retry limit?)r	   ÚtimeÚ"_WITH_TRANSACTION_RETRY_TIME_LIMIT)Ú
start_timer   r   r    Ú_within_time_limitK  s   rm   c                   @   s4  e Zd ZdZdd„ Zdd„ Zdd„ Zdd	„ Zd
d„ Zdd„ Z	e
dd„ ƒZe
dd„ ƒZe
dd„ ƒZe
dd„ ƒZe
dd„ ƒZdd„ Z		dAdd„Z		dAdd„Zdd „ Zd!d"„ Zd#d$„ Zd%d&„ Zd'd(„ Zd)d*„ Zd+d,„ Zd-d.„ Zd/d0„ Ze
d1d2„ ƒZe
d3d4„ ƒZe
d5d6„ ƒZd7d8„ Zd9d:„ Z d;d<„ Z!d=d>„ Z"d?d@„ Z#dS )BÚClientSessionz-A session for ordering sequential operations.c                 C   s8   || _ || _|| _|| _d | _d | _|| _td ƒ| _d S r9   )	Ú_clientÚ_server_sessionÚ_optionsÚ_authsetÚ_cluster_timeÚ_operation_timer4   rS   Ú_transaction)r   ÚclientÚserver_sessionÚoptionsÚauthsetÚimplicitr   r   r    r!   R  s   zClientSession.__init__c                 C   s   | j dd� dS )z�Finish this session. If a transaction has started, abort it.

        It is an error to use the session after the session has ended.
        T©ÚlockN©Ú_end_sessionr#   r   r   r    Úend_session^  s   zClientSession.end_sessionc              
   C   sn   | j d ur5z#| jr|  ¡  W | j | j |¡ d | _ d S W | j | j |¡ d | _ d S | j | j |¡ d | _ w d S r9   )rp   r>   r@   ro   Ú_return_server_session)r   r|   r   r   r    r~   e  s   


ü
ÿúzClientSession._end_sessionc                 C   s   | j d u r	tdƒ‚d S )NzCannot use ended session)rp   r   r#   r   r   r    Ú_check_endedn  s   
ÿzClientSession._check_endedc                 C   r;   r9   r   r#   r   r   r    r<   r  r=   zClientSession.__enter__c                 C   s   | j dd� d S )NTr{   r}   rA   r   r   r    rE   u  s   zClientSession.__exit__c                 C   r"   )z^The :class:`~pymongo.mongo_client.MongoClient` this session was
        created from.
        )ro   r#   r   r   r    rv   x  ó   zClientSession.clientc                 C   r"   )z:The :class:`SessionOptions` this session was created with.)rq   r#   r   r   r    rx     r$   zClientSession.optionsc                 C   s   |   ¡  | jjS )z6A BSON document, the opaque server session identifier.)r�   rp   Ú
session_idr#   r   r   r    rƒ   „  s   zClientSession.session_idc                 C   r"   )zZThe cluster time returned by the last operation executed
        in this session.
        ©rs   r#   r   r   r    Úcluster_timeŠ  r‚   zClientSession.cluster_timec                 C   r"   )z\The operation time returned by the last operation executed
        in this session.
        ©rt   r#   r   r   r    Úoperation_time‘  r‚   zClientSession.operation_timec                 C   s2   |r|S | j j}|ot||ƒ}|r|S t| j|ƒS )z-Return the inherited TransactionOption value.)rx   r   Úgetattrrv   )r   ÚnameÚvalÚtxn_optsr   r   r    Ú_inherit_option˜  s   zClientSession._inherit_optionNc           	   
   C   sô   t  ¡ }	 |  ||||¡ z|| ƒ}W n( ty; } z| jr"|  ¡  t|tƒr6| d¡r6t	|ƒr6W Y d}~q‚ d}~ww | jsA|S 	 z|  
¡  W |S  tyx } z#| d¡rdt	|ƒrdt|ƒsdW Y d}~qA| d¡rst	|ƒrsW Y d}~n‚ d}~ww q)as  Execute a callback in a transaction.

        This method starts a transaction on this session, executes ``callback``
        once, and then commits the transaction. For example::

          def callback(session):
              orders = session.client.db.orders
              inventory = session.client.db.inventory
              orders.insert_one({"sku": "abc123", "qty": 100}, session=session)
              inventory.update_one({"sku": "abc123", "qty": {"$gte": 100}},
                                   {"$inc": {"qty": -100}}, session=session)

          with client.start_session() as session:
              session.with_transaction(callback)

        To pass arbitrary arguments to the ``callback``, wrap your callable
        with a ``lambda`` like this::

          def callback(session, custom_arg, custom_kwarg=None):
              # Transaction operations...

          with client.start_session() as session:
              session.with_transaction(
                  lambda s: callback(s, "custom_arg", custom_kwarg=1))

        In the event of an exception, ``with_transaction`` may retry the commit
        or the entire transaction, therefore ``callback`` may be invoked
        multiple times by a single call to ``with_transaction``. Developers
        should be mindful of this possiblity when writing a ``callback`` that
        modifies application state or has any other side-effects.
        Note that even when the ``callback`` is invoked multiple times,
        ``with_transaction`` ensures that the transaction will be committed
        at-most-once on the server.

        The ``callback`` should not attempt to start new transactions, but
        should simply run operations meant to be contained within a
        transaction. The ``callback`` should also not commit the transaction;
        this is handled automatically by ``with_transaction``. If the
        ``callback`` does commit or abort the transaction without error,
        however, ``with_transaction`` will return without taking further
        action.

        When ``callback`` raises an exception, ``with_transaction``
        automatically aborts the current transaction. When ``callback`` or
        :meth:`~ClientSession.commit_transaction` raises an exception that
        includes the ``"TransientTransactionError"`` error label,
        ``with_transaction`` starts a new transaction and re-executes
        the ``callback``.

        When :meth:`~ClientSession.commit_transaction` raises an exception with
        the ``"UnknownTransactionCommitResult"`` error label,
        ``with_transaction`` retries the commit until the result of the
        transaction is known.

        This method will cease retrying after 120 seconds has elapsed. This
        timeout is not configurable and any exception raised by the
        ``callback`` or by :meth:`ClientSession.commit_transaction` after the
        timeout is reached will be re-raised. Applications that desire a
        different timeout duration should not use this method.

        :Parameters:
          - `callback`: The callable ``callback`` to run inside a transaction.
            The callable must accept a single argument, this session. Note,
            under certain error conditions the callback may be run multiple
            times.
          - `read_concern` (optional): The
            :class:`~pymongo.read_concern.ReadConcern` to use for this
            transaction.
          - `write_concern` (optional): The
            :class:`~pymongo.write_concern.WriteConcern` to use for this
            transaction.
          - `read_preference` (optional): The read preference to use for this
            transaction. If ``None`` (the default) the :attr:`read_preference`
            of this :class:`Database` is used. See
            :mod:`~pymongo.read_preferences` for options.

        :Returns:
          The return value of the ``callback``.

        .. versionadded:: 3.9
        TÚTransientTransactionErrorNr]   )r	   rj   Ústart_transactionÚ	Exceptionr>   r@   r   r   Úhas_error_labelrm   r?   rg   )	r   Úcallbackr/   r0   r1   r2   rl   Úretrc   r   r   r    Úwith_transaction¢  sR   Sþ
ÿþ€ø

ò
ÿþ
ÿ€õézClientSession.with_transactionc                 C   sŠ   |   ¡  | jrtdƒ‚|  d|¡}|  d|¡}|  d|¡}|du r*| jj}|r*|j}t||||ƒ| j_	| j 
¡  tj| j_|  ¡  t| ƒS )zãStart a multi-statement transaction.

        Takes the same arguments as :class:`TransactionOptions`.

        .. versionchanged:: 3.9
           Added the ``max_commit_time_ms`` option.

        .. versionadded:: 3.7
        zTransaction already in progressr/   r0   r1   N)r�   r>   r   rŒ   rx   r   r2   r   ru   rU   r\   rF   rN   rV   Ú_start_retryable_writer8   )r   r/   r0   r1   r2   rU   r   r   r    rŽ     s&   ÿÿ

zClientSession.start_transactionc              
   C   sd  |   ¡  d}| jj}|tju rtdƒ‚|tjtjfv r"tj| j_dS |tju r+tdƒ‚|tj	u r7tj
| j_d}ztz|  d|¡ W nK ty[ } z| d¡ t|ƒ W Y d}~n=d}~w typ } z
t|ƒ W Y d}~n0d}~w ty‹ } z|jtvr}‚ t|ƒ W Y d}~nd}~ww W tj	| j_dS W tj	| j_dS W tj	| j_dS W tj	| j_dS tj	| j_w )zMCommit a multi-statement transaction.

        .. versionadded:: 3.7
        FúNo transaction startedNz<Cannot call commitTransaction after calling abortTransactionTÚcommitTransactionr�   )r�   ru   rV   rF   rM   r   rN   rQ   rR   rP   rO   Ú_finish_transaction_with_retryr   Ú_remove_error_labelrd   r   r   rf   Ú_UNKNOWN_COMMIT_ERROR_CODES)r   ÚretryrV   rc   r   r   r    r?   =  sL   


ÿ


€€
€ùõòö
þz ClientSession.commit_transactionc              	   C   sº   |   ¡  | jj}|tju rtdƒ‚|tju rtj| j_dS |tju r&tdƒ‚|tjtj	fv r2tdƒ‚z$z|  
dd¡ W n ttfyF   Y n	w W tj| j_dS W tj| j_dS tj| j_w )zLAbort a multi-statement transaction.

        .. versionadded:: 3.7
        r•   Nz"Cannot call abortTransaction twicez<Cannot call abortTransaction after calling commitTransactionÚabortTransactionF)r�   ru   rV   rF   rM   r   rN   rR   rP   rQ   r—   r   r   )r   rV   r   r   r    r@   k  s,   



ÿþÿþzClientSession.abort_transactionc                 C   s´   z|   ||¡W S  ty   ‚  ty1 } zz|   |d¡W W  Y d}~S  ty,   |‚w d}~w tyY } z|jtvr>‚ z|   |d¡W W  Y d}~S  tyT   |‚w d}~ww )aD  Run commit or abort with one retry after any retryable error.

        :Parameters:
          - `command_name`: Either "commitTransaction" or "abortTransaction".
          - `explict_retry`: True when this is an explict commit retry attempt,
            ie the application called session.commit_transaction() twice.
        TN)Ú_finish_transactionr   r   r   rf   r   )r   Úcommand_nameÚexplict_retryrc   r   r   r    r—   ‡  s*   	ý€
ý€ûz,ClientSession._finish_transaction_with_retryc                 C   s¼   | j j}|j}t|dfgƒ}|dkr0|jr|j|d< |r0|j}d|d< | dd¡ tdi |¤Ž}| j jr:| j j|d< | j	 
| ¡�}| j	jj||| |d	d
�W  d   ƒ S 1 sWw   Y  d S )NrG   r–   Ú	maxTimeMSÚmajorityÚwÚwtimeouti'  ÚrecoveryTokenT)r5   r0   Úparse_write_concern_errorr   )ru   rU   r0   r   r2   ÚdocumentÚ
setdefaultr   rY   ro   Ú_socket_for_writesÚadminÚ_command)r   r�   ÚretryingrU   ÚwcÚcmdÚwc_docÚ	sock_infor   r   r    rœ   ¥  s,   
û$ÿz!ClientSession._finish_transactionc                 C   s@   | j du r
|| _ dS |dur|d | j d kr|| _ dS dS dS )zInternal cluster time helper.NÚclusterTimer„   ©r   r…   r   r   r    Ú_advance_cluster_timeÁ  s   


þz#ClientSession._advance_cluster_timec                 C   s:   t |tjƒs
tdƒ‚t | d¡tƒstdƒ‚|  |¡ dS )zâUpdate the cluster time for this session.

        :Parameters:
          - `cluster_time`: The
            :data:`~pymongo.client_session.ClientSession.cluster_time` from
            another `ClientSession` instance.
        z6cluster_time must be a subclass of collections.Mappingr¯   zInvalid cluster_timeN)r   r   ÚMappingr   Úgetr   Ú
ValueErrorr±   r°   r   r   r    Úadvance_cluster_timeÉ  s   ÿz"ClientSession.advance_cluster_timec                 C   s8   | j du r
|| _ dS |dur|| j kr|| _ dS dS dS )zInternal operation time helper.Nr†   ©r   r‡   r   r   r    Ú_advance_operation_timeØ  s   



þz%ClientSession._advance_operation_timec                 C   s    t |tƒs	tdƒ‚|  |¡ dS )zèUpdate the operation time for this session.

        :Parameters:
          - `operation_time`: The
            :data:`~pymongo.client_session.ClientSession.operation_time` from
            another `ClientSession` instance.
        z>operation_time must be an instance of bson.timestamp.TimestampN)r   r   r   r·   r¶   r   r   r    Úadvance_operation_timeà  s   
z$ClientSession.advance_operation_timec                 C   sT   |   | d¡¡ |  | d¡¡ | jr$| jjr&| d¡}|r(|| j_dS dS dS dS )z?Process a response to a command that was run with this session.z$clusterTimeÚoperationTimer£   N)r±   r³   r·   r>   ru   rW   rY   )r   ÚreplyrY   r   r   r    Ú_process_responseí  s   
ýzClientSession._process_responsec                 C   s
   | j du S )z!True if this session is finished.N)rp   r#   r   r   r    Ú	has_endedö  ó   
zClientSession.has_endedc                 C   s
   | j  ¡ S )zhTrue if this session has an active multi-statement transaction.

        .. versionadded:: 3.10
        )ru   rZ   r#   r   r   r    r>   û  ó   
zClientSession.in_transactionc                 C   s   | j  ¡ r	| j jS dS )z3The mongos address this transaction was created on.N)ru   rZ   rX   r#   r   r   r    Ú_pinned_address  s   
zClientSession._pinned_addressc                 C   s   d| j _|jj| j _dS )z,Pin this session to the given mongos Server.TN)ru   rW   ÚdescriptionÚaddressrX   )r   Úserverr   r   r    Ú_pin_mongos
  s   zClientSession._pin_mongosc                 C   s   d| j _dS )z2Unpin this session from any pinned mongos address.N)ru   rX   r#   r   r   r    Ú_unpin_mongos  s   zClientSession._unpin_mongosc                 C   s   | j r| jjjS dS )z3Return read preference of this transaction or None.N)r>   ru   rU   r1   r#   r   r   r    Ú_txn_read_preference  s   
z"ClientSession._txn_read_preferencec                 C   sâ   |   ¡  t ¡ | j_| jj|d< | js| j ¡  |r"| jj	|d< d S | jro|t
jkr1td|f ƒ‚| jjtjkrctj| j_d|d< | jjjrM| jjjj}ni }| jjr]| jd ur]| j|d< |rc||d< | jj	|d< d|d	< d S d S )
NÚlsidÚ	txnNumberz9read preference in a transaction must be primary, not: %rTÚstartTransactionÚafterClusterTimeÚreadConcernFÚ
autocommit)r�   r	   rj   rp   Úlast_userƒ   r>   ru   r\   Útransaction_idr   ÚPRIMARYr   rV   rF   rN   rO   rU   r/   r¥   rx   r   r‡   )r   ÚcommandÚis_retryabler1   Úrcr   r   r    Ú	_apply_to  s:   

ÿÿ



èzClientSession._apply_toc                 C   s   |   ¡  | j ¡  d S r9   )r�   rp   Úinc_transaction_idr#   r   r   r    r”   @  s   z$ClientSession._start_retryable_writer3   )$r%   r&   r'   r(   r!   r   r~   r�   r<   rE   r)   rv   rx   rƒ   r…   r‡   rŒ   r“   rŽ   r?   r@   r—   rœ   r±   rµ   r·   r¸   r»   r¼   r>   r¿   rÃ   rÄ   rÅ   rÒ   r”   r   r   r   r    rn   P  sZ    	






ÿ{
ÿ .	


'rn   c                   @   s8   e Zd Zdd„ Zdd„ Zdd„ Zedd„ ƒZd	d
„ ZdS )Ú_ServerSessionc                 C   s6   dt t ¡ jdƒi| _t ¡ | _d| _d| _	|| _
d S )NÚidrJ   r   F)r   ÚuuidÚuuid4Úbytesrƒ   r	   rj   rÌ   Ú_transaction_idÚdirtyÚpool_id)r   rÛ   r   r   r    r!   F  s
   

z_ServerSession.__init__c                 C   s
   d| _ dS )zÂMark this session as dirty.

        A server session is marked dirty when a command fails with a network
        error. Dirty sessions are later discarded from the server session pool.
        TN)rÚ   r#   r   r   r    Ú
mark_dirtyN  r¾   z_ServerSession.mark_dirtyc                 C   s   t  ¡ | j }||d d kS )NrG   é<   )r	   rj   rÌ   )r   Úsession_timeout_minutesÚidle_secondsr   r   r    Ú	timed_outV  s   z_ServerSession.timed_outc                 C   s
   t | jƒS )zPositive 64-bit integer.)r   rÙ   r#   r   r   r    rÍ   \  r½   z_ServerSession.transaction_idc                 C   s   |  j d7  _ d S ©NrG   )rÙ   r#   r   r   r    rÓ   a  r[   z!_ServerSession.inc_transaction_idN)	r%   r&   r'   r!   rÜ   rà   r)   rÍ   rÓ   r   r   r   r    rÔ   E  s    
rÔ   c                       sP   e Zd ZdZ‡ fdd„Zdd„ Zdd„ Zdd	„ Zd
d„ Zdd„ Z	dd„ Z
‡  ZS )Ú_ServerSessionPoolzsPool of _ServerSession objects.

    This class is not thread-safe, access it while holding the Topology lock.
    c                    s    t t| ƒj|i |¤Ž d| _d S )Nr   )Úsuperrâ   r!   rÛ   )r   ÚargsÚkwargs©Ú	__class__r   r    r!   j  s   
z_ServerSessionPool.__init__c                 C   s   |  j d7  _ |  ¡  d S rá   )rÛ   Úclearr#   r   r   r    r\   n  s   z_ServerSessionPool.resetc                 C   s    g }| r|  |  ¡ j¡ | s|S r9   )ÚappendÚpoprƒ   )r   Úidsr   r   r    Úpop_allr  s
   ÿz_ServerSessionPool.pop_allc                 C   s2   |   |¡ | r|  ¡ }| |¡s|S | st| jƒS r9   )Ú_clear_staleÚpopleftrà   rÔ   rÛ   )r   rÞ   Úsr   r   r    Úget_server_sessionx  s   

ý
z%_ServerSessionPool.get_server_sessionc                 C   s&   |   |¡ | |¡s|  |¡ d S d S r9   )rí   rà   Úreturn_server_session_no_lock)r   rw   rÞ   r   r   r    Úreturn_server_sessionˆ  s   

ÿz(_ServerSessionPool.return_server_sessionc                 C   s(   |j | j kr|js|  |¡ d S d S d S r9   )rÛ   rÚ   Ú
appendleft)r   rw   r   r   r    rñ   �  s   ÿz0_ServerSessionPool.return_server_session_no_lockc                 C   s,   | r| d   |¡r|  ¡  nd S | sd S d S )Néÿÿÿÿ)rà   rê   )r   rÞ   r   r   r    rí   “  s
   
ûz_ServerSessionPool._clear_stale)r%   r&   r'   r(   r!   r\   rì   rð   rò   rñ   rí   Ú__classcell__r   r   ræ   r    râ   e  s    râ   )5r(   ÚcollectionsÚosr`   rÖ   Úbson.binaryr   Ú
bson.int64r   Úbson.py3compatr   r   r   Úbson.sonr   Úbson.timestampr   Úpymongor	   Úpymongo.errorsr
   r   r   r   r   r   r   Úpymongo.helpersr   Úpymongo.read_concernr   Úpymongo.read_preferencesr   r   Úpymongo.write_concernr   Úobjectr   r   r6   r8   rF   rS   rd   rg   Ú	frozensetr™   rk   rm   rn   rÔ   Údequerâ   r   r   r   r    Ú<module>   sH   R$$R	þ	   x 