U
    dΫ                     @  sN  U d Z ddlmZ ddlZddlZddlZddlmZ ddl	m
Z
mZmZmZmZ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 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' ddl(m)Z) ddl*m+Z+ ddl,m-Z-m.Z. ddl/m0Z0 ddl1m2Z2 e
rPddl3m4Z4 ddl5m6Z6 ddl7m8Z8 ddl9m:Z:m;Z; G dd dZ<G dd dZ=ddddddZ>G dd  d Z?G d!d" d"Z@G d#d$ d$ZAd%d&d'd(d)ZBd*d+d'd,d-ZCe)eDd.d/gB ZEd0eFd1< d2ZGd3d+d4d5d6ZHed7ZIe
rdd8lJmKZK G d9d: d:ZLG d;d< d<ZMG d=d> d>ZNG d?d@ d@ejOZPdS )Aa  Logical sessions for ordering sequential operations.

.. 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.

.. seealso:: The MongoDB documentation on `causal-consistency <https://dochub.mongodb.org/core/causal-consistency>`_.

.. _transactions-ref:

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

.. versionadded:: 3.7

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`.

In general, multi-document transactions only support read/write (CRUD)
operations on existing collections. However, MongoDB 4.4 adds support for
creating collections and indexes with some limitations, including an
insert operation that would result in the creation of a new collection.
For a complete description of all the supported and unsupported operations
see the `MongoDB server's documentation for transactions
<http://dochub.mongodb.org/core/transactions>`_.

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

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

.. versionadded:: 3.9

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.

.. seealso:: The MongoDB documentation on `transactions <https://dochub.mongodb.org/core/transactions>`_.

.. _snapshot-reads-ref:

Snapshot Reads
==============

.. versionadded:: 3.12

MongoDB 5.0 adds support for snapshot reads. Snapshot reads are requested by
passing the ``snapshot`` option to
:meth:`~pymongo.mongo_client.MongoClient.start_session`.
If ``snapshot`` is True, all read operations that use this session read data
from the same snapshot timestamp. The server chooses the latest
majority-committed snapshot timestamp when executing the first read operation
using the session. Subsequent reads on this session read from the same
snapshot timestamp. Snapshot reads are also supported when reading from
replica set secondaries.

.. code-block:: python

  # Each read using this session reads data from the same point in time.
  with client.start_session(snapshot=True) as session:
      order = orders.find_one({"sku": "abc123"}, session=session)
      inventory = inventory.find_one({"sku": "abc123"}, session=session)

Snapshot Reads Limitations
^^^^^^^^^^^^^^^^^^^^^^^^^^

Snapshot reads sessions are incompatible with ``causal_consistency=True``.
Only the following read operations are supported in a snapshot reads session:

- :meth:`~pymongo.collection.Collection.find`
- :meth:`~pymongo.collection.Collection.find_one`
- :meth:`~pymongo.collection.Collection.aggregate`
- :meth:`~pymongo.collection.Collection.count_documents`
- :meth:`~pymongo.collection.Collection.distinct` (on unsharded collections)

Classes
=======
    )annotationsN)Mapping)TYPE_CHECKINGAnyCallableContextManagerDictListr   MutableMappingNoReturnOptionalTypeTypeVar)Binary)Int64)SON)	Timestamp)_csot)_ConnectionManager)ConfigurationErrorConnectionFailureInvalidOperationOperationFailurePyMongoErrorWTimeoutError)_RETRYABLE_ERROR_CODES)ReadConcern)ReadPreference_ServerMode)SERVER_TYPE)WriteConcern)TracebackType)
Connection)Server)ClusterTime_Addressc                   @  s\   e Zd ZdZdddddddd	Zed
dddZeddddZeddddZdS )SessionOptionsa  Options for a new :class:`ClientSession`.

    :Parameters:
      - `causal_consistency` (optional): If True, read operations are causally
        ordered within the session. Defaults to True when the ``snapshot``
        option is ``False``.
      - `default_transaction_options` (optional): The default
        TransactionOptions to use for transactions started on this session.
      - `snapshot` (optional): If True, then all reads performed using this
        session will read from the same snapshot. This option is incompatible
        with ``causal_consistency=True``. Defaults to ``False``.

    .. versionchanged:: 3.12
       Added the ``snapshot`` parameter.
    NFzOptional[bool]zOptional['TransactionOptions']None)causal_consistencydefault_transaction_optionssnapshotreturnc                 C  sX   |r|rt dd}n|d kr"d}|| _|d k	rHt|tsHtd||| _|| _d S )Nz5snapshot reads do not support causal_consistency=TrueFTzgdefault_transaction_options must be an instance of pymongo.client_session.TransactionOptions, not: {!r})r   _causal_consistency
isinstanceTransactionOptions	TypeErrorformat_default_transaction_options	_snapshot)selfr(   r)   r*    r4   :/tmp/pip-unpacked-wheel-oblwsawz/pymongo/client_session.py__init__   s     
zSessionOptions.__init__boolr+   c                 C  s   | j S )z)Whether causal consistency is configured.)r,   r3   r4   r4   r5   r(      s    z!SessionOptions.causal_consistencyc                 C  s   | j S )zThe default TransactionOptions to use for transactions started on
        this session.

        .. versionadded:: 3.7
        )r1   r9   r4   r4   r5   r)      s    z*SessionOptions.default_transaction_optionsc                 C  s   | j S )zOWhether snapshot reads are configured.

        .. versionadded:: 3.12
        )r2   r9   r4   r4   r5   r*      s    zSessionOptions.snapshot)NNF)	__name__
__module____qualname____doc__r6   propertyr(   r)   r*   r4   r4   r4   r5   r&      s      r&   c                   @  sp   e Zd ZdZdddddddd	d
ZeddddZeddddZeddddZedd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
    NOptional[ReadConcern]Optional[WriteConcern]Optional[_ServerMode]Optional[int]r'   read_concernwrite_concernread_preferencemax_commit_time_msr+   c                 C  s   || _ || _|| _|| _|d k	r8t|ts8td||d k	rlt|tsXtd||j	slt
d||d k	rt|tstd||d k	rt|tstdd S )NzOread_concern must be an instance of pymongo.read_concern.ReadConcern, not: {!r}zRwrite_concern must be an instance of pymongo.write_concern.WriteConcern, not: {!r}z>transactions do not support unacknowledged write concern: {!r}zV{!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/   r0   r    acknowledgedr   r   int)r3   rD   rE   rF   rG   r4   r4   r5   r6     sD    



zTransactionOptions.__init__r8   c                 C  s   | j S )z>This transaction's :class:`~pymongo.read_concern.ReadConcern`.)rH   r9   r4   r4   r5   rD   <  s    zTransactionOptions.read_concernc                 C  s   | j S )z@This transaction's :class:`~pymongo.write_concern.WriteConcern`.)rI   r9   r4   r4   r5   rE   A  s    z TransactionOptions.write_concernc                 C  s   | j S )zEThis transaction's :class:`~pymongo.read_preferences.ReadPreference`.)rJ   r9   r4   r4   r5   rF   F  s    z"TransactionOptions.read_preferencec                 C  s   | j S )zfThe maxTimeMS to use when running a commitTransaction command.

        .. versionadded:: 3.9
        )rK   r9   r4   r4   r5   rG   K  s    z%TransactionOptions.max_commit_time_ms)NNNN)
r:   r;   r<   r=   r6   r>   rD   rE   rF   rG   r4   r4   r4   r5   r.      s       'r.   Optional[ClientSession]r@   )sessionrE   r+   c                 C  s.   | r*|dk	r*|j s*| jrdS td|| S )zValidate that an explicit session is not used with an unack'ed write.

    Returns the session to use for the next operation.
    NzJExplicit sessions are incompatible with unacknowledged write concern: {!r})rL   	_implicitr   r0   )rO   rE   r4   r4   r5   _validate_session_write_concernT  s    rQ   c                   @  s@   e Zd ZdZddddZd dddZd	d
dddddZdS )_TransactionContextz;Internal transaction context manager for start_transaction.ClientSession)rO   c                 C  s
   || _ d S N)_TransactionContext__session)r3   rO   r4   r4   r5   r6   o  s    z_TransactionContext.__init__r8   c                 C  s   | S rT   r4   r9   r4   r4   r5   	__enter__r  s    z_TransactionContext.__enter__zOptional[Type[BaseException]]zOptional[BaseException]zOptional[TracebackType]r'   exc_typeexc_valexc_tbr+   c                 C  s*   | j jr&|d kr| j   n
| j   d S rT   )rU   in_transactioncommit_transactionabort_transactionr3   rX   rY   rZ   r4   r4   r5   __exit__u  s    z_TransactionContext.__exit__N)r:   r;   r<   r=   r6   rV   r_   r4   r4   r4   r5   rR   l  s   rR   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ABORTEDr4   r4   r4   r5   r`     s   r`   c                   @  s   e Zd ZdZdddddZddd	d
ZddddZeddddZddddddZ	ddddZ
ddddZddddZdS )_TransactionzBInternal class to hold transaction information in a ClientSession.zOptional[TransactionOptions]MongoClient)optsclientc                 C  s6   || _ tj| _d| _d | _d | _d | _d| _|| _	d S NFr   )
ro   r`   rg   stateshardedpinned_addressconn_mgrrecovery_tokenattemptrp   )r3   ro   rp   r4   r4   r5   r6     s    z_Transaction.__init__r7   r8   c                 C  s   | j tjtjfkS rT   )rr   r`   rh   ri   r9   r4   r4   r5   active  s    z_Transaction.activec                 C  s   | j tjkS rT   )rr   r`   rh   r9   r4   r4   r5   starting  s    z_Transaction.startingOptional[Connection]c                 C  s   |   r| jr| jjS d S rT   )rx   ru   connr9   r4   r4   r5   pinned_conn  s    z_Transaction.pinned_connr#   r"   r'   serverr{   r+   c                 C  s6   d| _ |jj| _|jjtjkr2|  t|d| _	d S )NTF)
rs   descriptionaddressrt   Zserver_typer   ZLoadBalancerZpin_txnr   ru   r3   r~   r{   r4   r4   r5   pin  s
    
z_Transaction.pinc                 C  s    d | _ | jr| j  d | _d S rT   )rt   ru   closer9   r4   r4   r5   unpin  s    
z_Transaction.unpinc                 C  s&   |    tj| _d| _d | _d| _d S rq   )r   r`   rg   rr   rs   rv   rw   r9   r4   r4   r5   reset  s
    z_Transaction.resetc                 C  s"   | j r| jdd | j  d | _ d S Nr   )ru   rp   Z_close_cursor_soonr9   r4   r4   r5   __del__  s    z_Transaction.__del__N)r:   r;   r<   r=   r6   rx   ry   r>   r|   r   r   r   r   r4   r4   r4   r5   rm     s   
rm   r   r   )excr+   c                 C  s   |  d  dS )zDRe-raise an exception with the UnknownTransactionCommitResult label.UnknownTransactionCommitResultN)Z_add_error_labelr   r4   r4   r5   _reraise_with_unknown_commit  s    
r   r   r7   c                 C  s   t | to| jdkS )z/Return true if exc is a MaxTimeMSExpired error.2   )r-   r   coder   r4   r4   r5   _max_time_expired_error  s    r   @   r   	frozenset_UNKNOWN_COMMIT_ERROR_CODESx   float)
start_timer+   c                 C  s   t  |  tk S )z/Are we within the with_transaction retry limit?)time	monotonic"_WITH_TRANSACTION_RETRY_TIME_LIMIT)r   r4   r4   r5   _within_time_limit  s    r   _T)rn   c                   @  sz  e Zd ZdZdddddddd	Zdd
ddZdddddZdd
ddZdd
ddZdddddddZ	e
dd
ddZe
dd
ddZe
dd
ddZe
dd
d d!Ze
d"d
d#d$Zd%d&d&d'd(d)Zdud+d,d-d.d/d&d0d1d2Zdvd,d-d.d/d3d4d5d6Zdd
d7d8Zdd
d9d:Zd%d;d<d=d>Zd?d%d;d@dAdBZdCddDdEdFZdddDdGdHZd"ddIdJdKZdLddIdMdNZdddOdPdQZe
dd
dRdSZe
dd
dTdUZe
dd
dVdWZe
dXd
dYdZZe
d[d
d\d]Z d^d?dd_d`daZ!dd
dbdcZ"d.d
dddeZ#dd
dfdgZ$dhddid?ddjdkdlZ%dd
dmdnZ&dhd?ddodpdqZ'drd
dsdtZ(d*S )wrS   a  A session for ordering sequential operations.

    :class:`ClientSession` instances are **not thread-safe or fork-safe**.
    They can only be used by one thread or process at a time. A single
    :class:`ClientSession` cannot be used to run multiple operations
    concurrently.

    Should not be initialized directly by application developers - to create a
    :class:`ClientSession`, call
    :meth:`~pymongo.mongo_client.MongoClient.start_session`.
    rn   r   r&   r7   r'   )rp   server_sessionoptionsimplicitr+   c                 C  s:   || _ || _|| _d | _d | _d | _|| _td || _d S rT   )	_client_server_session_options_cluster_time_operation_time_snapshot_timerP   rm   _transaction)r3   rp   r   r   r   r4   r4   r5   r6     s    zClientSession.__init__r8   c                 C  s   | j dd dS )zFinish 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_sessionr9   r4   r4   r5   end_session  s    zClientSession.end_session)r   r+   c              
   C  sB   | j d k	r>z| jr|   |   W 5 | j| j | d | _ X d S rT   )r   r   Z_return_server_sessionr[   r]   _unpin)r3   r   r4   r4   r5   r     s    
zClientSession._end_sessionc                 C  s   | j d krtdd S )NzCannot use ended session)r   r   r9   r4   r4   r5   _check_ended  s    
zClientSession._check_endedz'ClientSession'c                 C  s   | S rT   r4   r9   r4   r4   r5   rV     s    zClientSession.__enter__rW   c                 C  s   | j dd d S )NTr   r   r^   r4   r4   r5   r_     s    zClientSession.__exit__c                 C  s   | j S )z^The :class:`~pymongo.mongo_client.MongoClient` this session was
        created from.
        )r   r9   r4   r4   r5   rp   "  s    zClientSession.clientc                 C  s   | j S )z:The :class:`SessionOptions` this session was created with.)r   r9   r4   r4   r5   r   )  s    zClientSession.optionszMapping[str, Any]c                 C  s   |    | jjS )z6A BSON document, the opaque server session identifier.)r   r   
session_idr9   r4   r4   r5   r   .  s    zClientSession.session_idzOptional[ClusterTime]c                 C  s   | j S )zZThe cluster time returned by the last operation executed
        in this session.
        r   r9   r4   r4   r5   cluster_time4  s    zClientSession.cluster_timezOptional[Timestamp]c                 C  s   | j S )z\The operation time returned by the last operation executed
        in this session.
        r   r9   r4   r4   r5   operation_time;  s    zClientSession.operation_timestrr   )namevalr+   c                 C  s2   |r|S | j j}|ot||}|r&|S t| j|S )z-Return the inherited TransactionOption value.)r   r)   getattrrp   )r3   r   r   Ztxn_optsZ
parent_valr4   r4   r5   _inherit_optionB  s    zClientSession._inherit_optionNzCallable[['ClientSession'], _T]r?   r@   rA   rB   )callbackrD   rE   rF   rG   r+   c           	   
   C  s   t  }| |||| z|| }W nV tk
rz } z8| jrD|   t|trh|drht	|rhW Y q W 5 d}~X Y nX | js|S z| 
  W n` tk
r } zB|drt	|rt|sW Y "q|drt	|rW Y q W 5 d}~X Y nX |S qdS )a1  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 possibility 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.

        :class:`ClientSession` instances are **not thread-safe or fork-safe**.
        Consequently, the ``callback`` must not attempt to execute multiple
        operations concurrently.

        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
        TransientTransactionErrorNr   )r   r   start_transaction	Exceptionr[   r]   r-   r   Zhas_error_labelr   r\   r   )	r3   r   rD   rE   rF   rG   r   retr   r4   r4   r5   with_transactionL  sB    ]zClientSession.with_transactionr   rC   c                 C  s   |    | jjrtd| jr&td| d|}| d|}| d|}|dkrd| jj}|rd|j}t||||| j	_
| j	  tj| j	_|   t| S )zStart a multi-statement transaction.

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

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

        .. versionadded:: 3.7
        z3Transactions are not supported in snapshot sessionszTransaction already in progressrD   rE   rF   N)r   r   r*   r   r[   r   r)   rG   r.   r   ro   r   r`   rh   rr   _start_retryable_writerR   )r3   rD   rE   rF   rG   ro   r4   r4   r5   r     s,       

zClientSession.start_transactionc              
   C  s"  |    | jj}|tjkr$tdnF|tjtjfkrBtj| j_dS |tjkrVtdn|tj	krjtj
| j_zz| d W n tk
r } z|d t| W 5 d}~X Y n` tk
r } zt| W 5 d}~X Y n8 tk
r } z|jtkr t| W 5 d}~X Y nX W 5 tj	| j_X dS )zMCommit a multi-statement transaction.

        .. versionadded:: 3.7
        No transaction startedNz<Cannot call commitTransaction after calling abortTransactioncommitTransactionr   )r   r   rr   r`   rg   r   rh   rk   rl   rj   ri   _finish_transaction_with_retryr   Z_remove_error_labelr   r   r   r   r   )r3   rr   r   r4   r4   r5   r\     s.    








z ClientSession.commit_transactionc              	   C  s   |    | jj}|tjkr$tdnD|tjkr<tj| j_dS |tjkrPtdn|tjtj	fkrhtdz,z| d W n ttfk
r   Y nX W 5 tj| j_| 
  X dS )zLAbort a multi-statement transaction.

        .. versionadded:: 3.7
        r   Nz"Cannot call abortTransaction twicez<Cannot call abortTransaction after calling commitTransactionZabortTransaction)r   r   rr   r`   rg   r   rh   rl   rj   rk   r   r   r   r   )r3   rr   r4   r4   r5   r]   '  s"    







zClientSession.abort_transactionDict[str, Any])command_namer+   c                   s,   ddddd fdd}j d|d	S )
zRun commit or abort with one retry after any retryable error.

        :Parameters:
          - `command_name`: Either "commitTransaction" or "abortTransaction".
        rN   r"   r7   r   )rO   r{   	retryabler+   c                   s    | S rT   )_finish_transaction)rO   r{   r   r   r3   r4   r5   funcJ  s    z:ClientSession._finish_transaction_with_retry.<locals>.funcTN)r   Z_retry_internal)r3   r   r   r4   r   r5   r   C  s    z,ClientSession._finish_transaction_with_retryr"   )r{   r   r+   c                 C  s   | j  jd7  _| j j}|s t|j}t|dfg}|dkr|jrXt d krX|j|d< | j jdkr|slt|j	}d|d< |
dd tf |}| j jr| j j|d< | jjj||| |d	d
S )Nra   r   Z	maxTimeMSZmajoritywZwtimeouti'  recoveryTokenT)rO   rE   Zparse_write_concern_error)r   rw   ro   AssertionErrorrE   r   rG   r   Zget_timeoutdocument
setdefaultr    rv   r   ZadminZ_command)r3   r{   r   ro   ZwccmdZwc_docr4   r4   r5   r   Q  s.    

    z!ClientSession._finish_transactionzOptional[Mapping[str, Any]])r   r+   c                 C  s6   | j dkr|| _ n |dk	r2|d | j d kr2|| _ dS )zInternal cluster time helper.NclusterTimer   r3   r   r4   r4   r5   _advance_cluster_timel  s
    
z#ClientSession._advance_cluster_timec                 C  s8   t |tstdt |dts*td| | dS )zUpdate 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-   _Mappingr/   getr   
ValueErrorr   r   r4   r4   r5   advance_cluster_timet  s
    
z"ClientSession.advance_cluster_time)r   r+   c                 C  s.   | j dkr|| _ n|dk	r*|| j kr*|| _ dS )zInternal operation time helper.Nr   r3   r   r4   r4   r5   _advance_operation_time  s
    

z%ClientSession._advance_operation_timer   c                 C  s    t |tstd| | dS )zUpdate 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   r4   r4   r5   advance_operation_time  s    
z$ClientSession.advance_operation_time)replyr+   c                 C  s   |  |d | |d | jjrZ| jdkrZd|krJ|d d}n
|d}|| _| jr~| jjr~|d}|r~|| j_	dS )z?Process a response to a command that was run with this session.z$clusterTimeZoperationTimeNcursoratClusterTimer   )
r   r   r   r   r*   r   r[   r   rs   rv   )r3   r   ctrv   r4   r4   r5   _process_response  s    

zClientSession._process_responsec                 C  s
   | j dkS )z!True if this session is finished.N)r   r9   r4   r4   r5   	has_ended  s    zClientSession.has_endedc                 C  s
   | j  S )zhTrue if this session has an active multi-statement transaction.

        .. versionadded:: 3.10
        )r   rx   r9   r4   r4   r5   r[     s    zClientSession.in_transactionc                 C  s
   | j  S )z?True if this session is starting a multi-statement transaction.)r   ry   r9   r4   r4   r5   _starting_transaction  s    z#ClientSession._starting_transactionzOptional[_Address]c                 C  s   | j  r| j jS dS )z3The mongos address this transaction was created on.N)r   rx   rt   r9   r4   r4   r5   _pinned_address  s    
zClientSession._pinned_addressrz   c                 C  s   | j jS )z/The connection this transaction was started on.)r   r|   r9   r4   r4   r5   _pinned_connection  s    z ClientSession._pinned_connectionr#   r}   c                 C  s   | j || dS )z@Pin this session to the given Server or to the given connection.N)r   r   r   r4   r4   r5   _pin  s    zClientSession._pinc                 C  s   | j   dS )z*Unpin this session from any pinned Server.N)r   r   r9   r4   r4   r5   r     s    zClientSession._unpinc                 C  s    | j r| jjst| jjjS dS )z3Return read preference of this transaction or None.N)r[   r   ro   r   rF   r9   r4   r4   r5   _txn_read_preference  s    
z"ClientSession._txn_read_preferencec                 C  s4   t | jtr0| j}| jj | _|jr0| j  d S rT   )r-   r   _EmptyServerSessionr   Z	_topologyget_server_sessionstarted_retryable_writeinc_transaction_id)r3   oldr4   r4   r5   _materialize  s
    zClientSession._materializezMutableMapping[str, Any]r   )commandis_retryablerF   r{   r+   c                 C  s   |    |   | jjr$| || t | j_| jj	|d< |rP| jj
|d< d S | jr|tjkrntd|| jjtjkrtj| j_d|d< | jjst| jjjr| jjjj}|r||d< | || | jj
|d< d|d< d S )	NZlsidZ	txnNumberz;read preference in a transaction must be primary, not: {!r}TZstartTransactionreadConcernFZ
autocommit)r   r   r   r*   _update_read_concernr   r   r   last_user   transaction_idr[   r   ZPRIMARYr   r0   r   rr   r`   rh   ri   ro   r   rD   r   )r3   r   r   rF   r{   rcr4   r4   r5   	_apply_to  s6    


zClientSession._apply_toc                 C  s   |    | j  d S rT   )r   r   r   r9   r4   r4   r5   r     s    z$ClientSession._start_retryable_write)r   r{   r+   c                 C  sj   | j jr$| jd k	r$| j|di d< | j jrf|jdk r>td|di }d|d< | jd k	rf| j|d< d S )Nr   ZafterClusterTime   z+Snapshot reads require MongoDB 5.0 or laterr*   levelr   )r   r(   r   r   r*   Zmax_wire_versionr   r   )r3   r   r{   r   r4   r4   r5   r     s    

z"ClientSession._update_read_concernr   c                 C  s   t dd S )Nz>A ClientSession cannot be copied, create a new session instead)r/   r9   r4   r4   r5   __copy__  s    zClientSession.__copy__)NNNN)NNNN))r:   r;   r<   r=   r6   r   r   r   rV   r_   r>   rp   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   r   r   r4   r4   r4   r5   rS     sp            (+)rS   c                   @  s:   e Zd ZdZddddZddddZdddd	Zd
S )r   dirtyr   r'   r8   c                 C  s   d| _ d| _d S )NFr   r9   r4   r4   r5   r6     s    z_EmptyServerSession.__init__c                 C  s
   d| _ d S NTr   r9   r4   r4   r5   
mark_dirty  s    z_EmptyServerSession.mark_dirtyc                 C  s
   d| _ d S r   )r   r9   r4   r4   r5   r     s    z&_EmptyServerSession.inc_transaction_idN)r:   r;   r<   	__slots__r6   r   r   r4   r4   r4   r5   r     s   r   c                   @  sX   e Zd ZddddZddddZd	d
dddZeddddZddddZdS )_ServerSessionrM   )
generationc                 C  s6   dt t jdi| _t | _d| _d| _	|| _
d S )Nidrd   r   F)r   uuidZuuid4bytesr   r   r   r   _transaction_idr   r   )r3   r   r4   r4   r5   r6   $  s
    
z_ServerSession.__init__r'   r8   c                 C  s
   d| _ dS )zMark 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.
        TNr   r9   r4   r4   r5   r   ,  s    z_ServerSession.mark_dirtyr   r7   session_timeout_minutesr+   c                 C  s   t  | j }||d d kS )Nra   <   )r   r   r   )r3   r   Zidle_secondsr4   r4   r5   	timed_out4  s    z_ServerSession.timed_outr   c                 C  s
   t | jS )zPositive 64-bit integer.)r   r   r9   r4   r4   r5   r   :  s    z_ServerSession.transaction_idc                 C  s   |  j d7  _ d S Nra   )r   r9   r4   r4   r5   r   ?  s    z!_ServerSession.inc_transaction_idN)	r:   r;   r<   r6   r   r   r>   r   r   r4   r4   r4   r5   r   #  s   r   c                      s   e Zd ZdZddd fddZdddd	Zd
dddZdddddZddddddZdddddZ	dddddZ
  ZS )_ServerSessionPoolzsPool of _ServerSession objects.

    This class is not thread-safe, access it while holding the Topology lock.
    r   )argskwargsc                   s   t  j|| d| _d S r   )superr6   r   )r3   r  r  	__class__r4   r5   r6   I  s    z_ServerSessionPool.__init__r'   r8   c                 C  s   |  j d7  _ |   d S r  )r   clearr9   r4   r4   r5   r   M  s    z_ServerSessionPool.resetzList[_ServerSession]c                 C  s   g }| r| |  j q|S rT   )appendpopr   )r3   idsr4   r4   r5   pop_allQ  s    z_ServerSessionPool.pop_allr   r   r   c                 C  s0   |  | | r&|  }||s
|S q
t| jS rT   )_clear_stalepopleftr   r   r   )r3   r   sr4   r4   r5   r   W  s    

z%_ServerSessionPool.get_server_sessionzOptional[float])r   r   r+   c                 C  s.   |d k	r |  | ||r d S | | d S rT   )r  r   return_server_session_no_lock)r3   r   r   r4   r4   r5   return_server_sessiong  s
    

z(_ServerSessionPool.return_server_session)r   r+   c                 C  s    |j | j kr|js| | d S rT   )r   r   
appendleft)r3   r   r4   r4   r5   r  p  s    z0_ServerSessionPool.return_server_session_no_lockc                 C  s$   | r | d  |r |   q q q d S )N)r   r
  )r3   r   r4   r4   r5   r  v  s    
z_ServerSessionPool._clear_stale)r:   r;   r<   r=   r6   r   r  r   r  r  r  __classcell__r4   r4   r  r5   r  C  s   	r  )Qr=   
__future__r   collectionsr   r   collections.abcr   r   typingr   r   r   r   r   r	   r
   r   r   r   r   Zbson.binaryr   Z
bson.int64r   Zbson.sonr   Zbson.timestampr   Zpymongor   Zpymongo.cursorr   Zpymongo.errorsr   r   r   r   r   r   Zpymongo.helpersr   Zpymongo.read_concernr   Zpymongo.read_preferencesr   r   Zpymongo.server_typer   Zpymongo.write_concernr    typesr!   Zpymongo.poolr"   Zpymongo.serverr#   Zpymongo.typingsr$   r%   r&   r.   rQ   rR   r`   rm   r   r   r   r   __annotations__r   r   r   Zpymongo.mongo_clientrn   rS   r   r   dequer  r4   r4   r4   r5   <module>   sb   y8 @[	5    3 