U
    dS                     @  s  d 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 erddlmZ dd	lmZ dZd
ZdZdZdZdZe	eef Zee	eef  ZdddddZdddddZ dddddZ!dddddZ"G dd  d Z#G d!d" d"e#Z$G d#d$ d$e#Z%G d%d& d&e#Z&G d'd( d(e#Z'G d)d* d*e#Z(G d+d, d,Z)e$e%e&e'e(fZ*d:dddd d.d/d0Z+d1Z,G d2d3 d3Z-ddd4d5d6Z.G d7d8 d8Z/d9S );zBUtilities for choosing which member of a replica set to read from.    )annotations)abc)TYPE_CHECKINGAnyDictMappingOptionalSequence)max_staleness_selectors)ConfigurationError) member_with_tags_server_selector#secondary_with_tags_server_selector)	Selection)TopologyDescription            )primaryZprimaryPreferredZ	secondaryZsecondaryPreferredZnearestOptional[_TagSets])tag_setsreturnc                 C  sr   | dkr| S t | ttfs*td| dt| dkrFtd| d| D ]}t |tjsJtd|qJt| S )z$Validate tag sets for a MongoClient.Nz	Tag sets z invalid, must be a sequencer   z: invalid, must be None or contain at least one set of tagszsTag set {!r} invalid, must be an instance of dict, bson.son.SON or other type that inherits from collection.Mapping)	
isinstancelisttuple	TypeErrorlen
ValueErrorr   r   format)r   tags r    </tmp/pip-unpacked-wheel-oblwsawz/pymongo/read_preferences.py_validate_tag_sets4   s     
r"   r   str)max_stalenessr   c                 C  s   d|  S )Nz6maxStalenessSeconds must be a positive integer, not %sr    r$   r    r    r!   _invalid_max_staleness_msgK   s    r&   intc                 C  s:   | dkrdS t | ts"tt| | dkr6tt| | S )zValidate max_staleness.r   )r   r'   r   r&   r   r%   r    r    r!   _validate_max_stalenessP   s    
r)   Optional[_Hedge])hedger   c                 C  s(   | dkrdS t | ts$td| | S )zValidate hedge.Nz hedge must be a dictionary, not )r   dictr   )r+   r    r    r!   _validate_hedge^   s
    
r-   c                   @  s  e Zd ZdZdZd3d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eddddZeddddZeddddZddd d!Zd"d#d$d%d&Zd"d#d$d'd(Zddd)d*Zd+dd,d-d.Zd/d/d0d1d2ZdS )4_ServerModez$Base class for all read preferences.)Z__mongos_modeZ__modeZ
__tag_setsZ__max_stalenessZ__hedgeNr(   r'   r   r*   None)moder   r$   r+   r   c                 C  s2   t | | _|| _t|| _t|| _t|| _d S N)	_MONGOS_MODES_ServerMode__mongos_mode_ServerMode__moder"   _ServerMode__tag_setsr)   _ServerMode__max_stalenessr-   _ServerMode__hedge)selfr0   r   r$   r+   r    r    r!   __init__n   s
    


z_ServerMode.__init__r#   r   c                 C  s   | j jS )z!The name of this read preference.)	__class____name__r8   r    r    r!   name{   s    z_ServerMode.namec                 C  s   | j S )z(The mongos mode of this read preference.)r3   r=   r    r    r!   mongos_mode   s    z_ServerMode.mongos_modezDict[str, Any]c                 C  sT   d| j i}| jdi gfkr$| j|d< | jdkr8| j|d< | jdi fkrP| j|d< |S )zRead preference as a document.r0   Nr   r(   ZmaxStalenessSecondsr+   )r3   r5   r6   r7   )r8   docr    r    r!   document   s    




z_ServerMode.documentc                 C  s   | j S )z*The mode of this read preference instance.)r4   r=   r    r    r!   r0      s    z_ServerMode.mode_TagSetsc                 C  s   | j rt| j S i gS )aW  Set ``tag_sets`` to a list of dictionaries like [{'dc': 'ny'}] to
        read only from members whose ``dc`` tag has the value ``"ny"``.
        To specify a priority-order for tag sets, provide a list of
        tag sets: ``[{'dc': 'ny'}, {'dc': 'la'}, {}]``. A final, empty tag
        set, ``{}``, means "read from any member that matches the mode,
        ignoring tags." MongoClient tries each set of tags in turn
        until it finds a set of tags with at least one matching member.
        For example, to only send a query to an analytic node::

           Nearest(tag_sets=[{"node":"analytics"}])

        Or using :class:`SecondaryPreferred`::

           SecondaryPreferred(tag_sets=[{"node":"analytics"}])

           .. seealso:: `Data-Center Awareness
               <https://www.mongodb.com/docs/manual/data-center-awareness/>`_
        )r5   r   r=   r    r    r!   r      s    z_ServerMode.tag_setsc                 C  s   | j S )zThe maximum estimated length of time (in seconds) a replica set
        secondary can fall behind the primary in replication before it will
        no longer be selected for operations, or -1 for no maximum.
        r6   r=   r    r    r!   r$      s    z_ServerMode.max_stalenessc                 C  s   | j S )a  The read preference ``hedge`` parameter.

        A dictionary that configures how the server will perform hedged reads.
        It consists of the following keys:

        - ``enabled``: Enables or disables hedged reads in sharded clusters.

        Hedged reads are automatically enabled in MongoDB 4.4+ when using a
        ``nearest`` read preference. To explicitly enable hedged reads, set
        the ``enabled`` key  to ``true``::

            >>> Nearest(hedge={'enabled': True})

        To explicitly disable hedged reads, set the ``enabled`` key  to
        ``False``::

            >>> Nearest(hedge={'enabled': False})

        .. versionadded:: 3.11
        )r7   r=   r    r    r!   r+      s    z_ServerMode.hedgec                 C  s   | j dkrdS dS )a  The wire protocol version the server must support.

        Some read preferences impose version requirements on all servers (e.g.
        maxStalenessSeconds requires MongoDB 3.4 / maxWireVersion 5).

        All servers' maxWireVersion must be at least this read preference's
        `min_wire_version`, or the driver raises
        :exc:`~pymongo.errors.ConfigurationError`.
        r(   r      rC   r=   r    r    r!   min_wire_version   s    z_ServerMode.min_wire_versionc                 C  s   d | j| j| j| jS )Nz1{}(tag_sets={!r}, max_staleness={!r}, hedge={!r}))r   r>   r5   r6   r7   r=   r    r    r!   __repr__   s    z_ServerMode.__repr__r   boolotherr   c                 C  s>   t |tr:| j|jko8| j|jko8| j|jko8| j|jkS tS r1   )r   r.   r0   r   r$   r+   NotImplementedr8   rI   r    r    r!   __eq__   s    



z_ServerMode.__eq__c                 C  s
   | |k S r1   r    rK   r    r    r!   __ne__   s    z_ServerMode.__ne__c                 C  s   | j | j| j| jdS )zeReturn value of object for pickling.

        Needed explicitly because __slots__() defined.
        )r0   r   r$   r+   )r4   r5   r6   r7   r=   r    r    r!   __getstate__   s
    z_ServerMode.__getstate__zMapping[str, Any])valuer   c                 C  sD   |d | _ t| j  | _t|d | _t|d | _t|d | _dS )zRestore from pickling.r0   r   r$   r+   N)	r4   r2   r3   r"   r5   r)   r6   r-   r7   )r8   rO   r    r    r!   __setstate__   s
    
z_ServerMode.__setstate__r   	selectionr   c                 C  s   |S r1   r    r8   rR   r    r    r!   __call__  s    z_ServerMode.__call__)Nr(   N)r<   
__module____qualname____doc__	__slots__r9   propertyr>   r?   rA   r0   r   r$   r+   rE   rF   rL   rM   rN   rP   rT   r    r    r    r!   r.   i   s8      
r.   c                      sX   e Zd ZdZdZdd fddZdddd	d
ZddddZdddddZ  Z	S )PrimaryaG  Primary read preference.

    * When directly connected to one mongod queries are allowed if the server
      is standalone or a replica set primary.
    * When connected to a mongos queries are sent to the primary of a shard.
    * When connected to a replica set queries are sent to the primary of
      the replica set.
    r    r/   r:   c                   s   t  t d S r1   )superr9   _PRIMARYr=   r;   r    r!   r9     s    zPrimary.__init__r   rQ   c                 C  s   |j S z*Apply this read preference to a Selection.)primary_selectionrS   r    r    r!   rT     s    zPrimary.__call__r#   c                 C  s   dS )Nz	Primary()r    r=   r    r    r!   rF     s    zPrimary.__repr__r   rG   rH   c                 C  s   t |tr|jtkS tS r1   )r   r.   r0   r\   rJ   rK   r    r    r!   rL     s    

zPrimary.__eq__)
r<   rU   rV   rW   rX   r9   rT   rF   rL   __classcell__r    r    r]   r!   rZ     s   	rZ   c                      sB   e Zd ZdZdZdddddd	 fd
dZdddddZ  ZS )PrimaryPreferreda  PrimaryPreferred read preference.

    * When directly connected to one mongod queries are allowed to standalone
      servers, to a replica set primary, or to replica set secondaries.
    * When connected to a mongos queries are sent to the primary of a shard if
      available, otherwise a shard secondary.
    * When connected to a replica set queries are sent to the primary if
      available, otherwise a secondary.

    .. note:: When a :class:`~pymongo.mongo_client.MongoClient` is first
      created reads will be routed to an available secondary until the
      primary of the replica set is discovered.

    :Parameters:
      - `tag_sets`: The :attr:`~tag_sets` to use if the primary is not
        available.
      - `max_staleness`: (integer, in seconds) The maximum estimated
        length of time a replica set secondary can fall behind the primary in
        replication before it will no longer be selected for operations.
        Default -1, meaning no maximum. If it is set, it must be at least
        90 seconds.
      - `hedge`: The :attr:`~hedge` to use if the primary is not available.

    .. versionchanged:: 3.11
       Added ``hedge`` parameter.
    r    Nr(   r   r'   r*   r/   r   r$   r+   r   c                   s   t  t||| d S r1   )r[   r9   _PRIMARY_PREFERREDr8   r   r$   r+   r]   r    r!   r9   @  s    zPrimaryPreferred.__init__r   rQ   c                 C  s&   |j r|jS t| jt| j|S dS (Apply this read preference to Selection.N)r   r_   r   r   r
   selectr$   rS   r    r    r!   rT   H  s     zPrimaryPreferred.__call__)Nr(   Nr<   rU   rV   rW   rX   r9   rT   r`   r    r    r]   r!   ra   "  s      ra   c                      sB   e Zd ZdZdZdddddd	 fd
dZdddddZ  ZS )	Secondarya  Secondary read preference.

    * When directly connected to one mongod queries are allowed to standalone
      servers, to a replica set primary, or to replica set secondaries.
    * When connected to a mongos queries are distributed among shard
      secondaries. An error is raised if no secondaries are available.
    * When connected to a replica set queries are distributed among
      secondaries. An error is raised if no secondaries are available.

    :Parameters:
      - `tag_sets`: The :attr:`~tag_sets` for this read preference.
      - `max_staleness`: (integer, in seconds) The maximum estimated
        length of time a replica set secondary can fall behind the primary in
        replication before it will no longer be selected for operations.
        Default -1, meaning no maximum. If it is set, it must be at least
        90 seconds.
      - `hedge`: The :attr:`~hedge` for this read preference.

    .. versionchanged:: 3.11
       Added ``hedge`` parameter.
    r    Nr(   r   r'   r*   r/   rb   c                   s   t  t||| d S r1   )r[   r9   
_SECONDARYrd   r]   r    r!   r9   k  s    zSecondary.__init__r   rQ   c                 C  s   t | jt| j|S rf   )r   r   r
   rg   r$   rS   r    r    r!   rT   s  s     zSecondary.__call__)Nr(   Nrh   r    r    r]   r!   ri   R  s      ri   c                      sB   e Zd ZdZdZdddddd	 fd
dZdddddZ  ZS )SecondaryPreferreda  SecondaryPreferred read preference.

    * When directly connected to one mongod queries are allowed to standalone
      servers, to a replica set primary, or to replica set secondaries.
    * When connected to a mongos queries are distributed among shard
      secondaries, or the shard primary if no secondary is available.
    * When connected to a replica set queries are distributed among
      secondaries, or the primary if no secondary is available.

    .. note:: When a :class:`~pymongo.mongo_client.MongoClient` is first
      created reads will be routed to the primary of the replica set until
      an available secondary is discovered.

    :Parameters:
      - `tag_sets`: The :attr:`~tag_sets` for this read preference.
      - `max_staleness`: (integer, in seconds) The maximum estimated
        length of time a replica set secondary can fall behind the primary in
        replication before it will no longer be selected for operations.
        Default -1, meaning no maximum. If it is set, it must be at least
        90 seconds.
      - `hedge`: The :attr:`~hedge` for this read preference.

    .. versionchanged:: 3.11
       Added ``hedge`` parameter.
    r    Nr(   r   r'   r*   r/   rb   c                   s   t  t||| d S r1   )r[   r9   _SECONDARY_PREFERREDrd   r]   r    r!   r9     s    zSecondaryPreferred.__init__r   rQ   c                 C  s(   t | jt| j|}|r|S |jS dS re   )r   r   r
   rg   r$   r_   )r8   rR   Zsecondariesr    r    r!   rT     s     zSecondaryPreferred.__call__)Nr(   Nrh   r    r    r]   r!   rl   z  s      rl   c                      sB   e Zd ZdZdZdddddd	 fd
dZdddddZ  ZS )Nearestav  Nearest read preference.

    * When directly connected to one mongod queries are allowed to standalone
      servers, to a replica set primary, or to replica set secondaries.
    * When connected to a mongos queries are distributed among all members of
      a shard.
    * When connected to a replica set queries are distributed among all
      members.

    :Parameters:
      - `tag_sets`: The :attr:`~tag_sets` for this read preference.
      - `max_staleness`: (integer, in seconds) The maximum estimated
        length of time a replica set secondary can fall behind the primary in
        replication before it will no longer be selected for operations.
        Default -1, meaning no maximum. If it is set, it must be at least
        90 seconds.
      - `hedge`: The :attr:`~hedge` for this read preference.

    .. versionchanged:: 3.11
       Added ``hedge`` parameter.
    r    Nr(   r   r'   r*   r/   rb   c                   s   t  t||| d S r1   )r[   r9   _NEARESTrd   r]   r    r!   r9     s    zNearest.__init__r   rQ   c                 C  s   t | jt| j|S rk   )r   r   r
   rg   r$   rS   r    r    r!   rT     s     zNearest.__call__)Nr(   Nrh   r    r    r]   r!   rn     s      rn   c                   @  s`   e Zd ZdZdZddddZddd	d
dZdddddZddddZdddddZ	dS )_AggWritePrefa	  Agg $out/$merge write preference.

    * If there are readable servers and there is any pre-5.0 server, use
      primary read preference.
    * Otherwise use `pref` read preference.

    :Parameters:
      - `pref`: The read preference to use on MongoDB 5.0+.
    )prefeffective_prefr.   rq   c                 C  s   || _ tj| _d S r1   )rq   ReadPreferencePRIMARYrr   )r8   rq   r    r    r!   r9     s    z_AggWritePref.__init__r   r/   )topology_descriptionr   c                 C  s4   |j }|tjr(|r(|dk r(tj| _n| j| _d S )N   )Zcommon_wire_versionZhas_readable_serverrt   PRIMARY_PREFERREDru   rr   rq   )r8   rv   Z	common_wvr    r    r!   selection_hook  s    

z_AggWritePref.selection_hookr   rQ   c                 C  s
   |  |S r^   )rr   rS   r    r    r!   rT     s    z_AggWritePref.__call__r#   r:   c                 C  s   d| j dS )Nz_AggWritePref(pref=)rs   r=   r    r    r!   rF     s    z_AggWritePref.__repr__r   r>   r   c                 C  s   t | j|S r1   )getattrrr   )r8   r>   r    r    r!   __getattr__  s    z_AggWritePref.__getattr__N)
r<   rU   rV   rW   rX   r9   ry   rT   rF   r}   r    r    r    r!   rp     s   
rp   r(   )r0   r   r$   r   c                 C  sB   | t kr4|d i gfkrtd|dkr.tdt S t|  ||S )Nz4Read preference primary cannot be combined with tagsr(   zCRead preference primary cannot be combined with maxStalenessSeconds)r\   r   rZ   _ALL_READ_PREFERENCES)r0   r   r$   r    r    r!   make_read_preference  s    r   )ru   rx   	SECONDARYSECONDARY_PREFERREDNEARESTc                   @  s.   e Zd ZdZe Ze Ze Z	e
 Ze ZdS )rt   a  An enum that defines some commonly used read preference modes.

    Apps can also create a custom read preference, for example::

       Nearest(tag_sets=[{"node":"analytics"}])

    See :doc:`/examples/high_availability` for code examples.

    A read preference is used in three cases:

    :class:`~pymongo.mongo_client.MongoClient` connected to a single mongod:

    - ``PRIMARY``: Queries are allowed if the server is standalone or a replica
      set primary.
    - All other modes allow queries to standalone servers, to a replica set
      primary, or to replica set secondaries.

    :class:`~pymongo.mongo_client.MongoClient` initialized with the
    ``replicaSet`` option:

    - ``PRIMARY``: Read from the primary. This is the default, and provides the
      strongest consistency. If no primary is available, raise
      :class:`~pymongo.errors.AutoReconnect`.

    - ``PRIMARY_PREFERRED``: Read from the primary if available, or if there is
      none, read from a secondary.

    - ``SECONDARY``: Read from a secondary. If no secondary is available,
      raise :class:`~pymongo.errors.AutoReconnect`.

    - ``SECONDARY_PREFERRED``: Read from a secondary if available, otherwise
      from the primary.

    - ``NEAREST``: Read from any member.

    :class:`~pymongo.mongo_client.MongoClient` connected to a mongos, with a
    sharded cluster of replica sets:

    - ``PRIMARY``: Read from the primary of the shard, or raise
      :class:`~pymongo.errors.OperationFailure` if there is none.
      This is the default.

    - ``PRIMARY_PREFERRED``: Read from the primary of the shard, or if there is
      none, read from a secondary of the shard.

    - ``SECONDARY``: Read from a secondary of the shard, or raise
      :class:`~pymongo.errors.OperationFailure` if there is none.

    - ``SECONDARY_PREFERRED``: Read from a secondary of the shard if available,
      otherwise from the shard primary.

    - ``NEAREST``: Read from any shard member.
    N)r<   rU   rV   rW   rZ   ru   ra   rx   ri   r   rl   r   rn   r   r    r    r    r!   rt     s   6rt   r{   c                 C  s
   t | S )z2Get the read preference mode from mongos/uri name.)r2   index)r>   r    r    r!   read_pref_mode_from_nameT  s    r   c                   @  sT   e Zd ZU dZded< ddddZddd	d
dZddddZddddZdS )MovingAveragez0Tracks an exponentially-weighted moving average.zOptional[float]averager/   r:   c                 C  s
   d | _ d S r1   r   r=   r    r    r!   r9   ^  s    zMovingAverage.__init__float)sampler   c                 C  s6   |dk rd S | j d kr|| _ nd| j  d|  | _ d S )Nr   g?g?r   )r8   r   r    r    r!   
add_samplea  s
    
zMovingAverage.add_samplec                 C  s   | j S )z6Get the calculated average, or None if no samples yet.r   r=   r    r    r!   getn  s    zMovingAverage.getc                 C  s
   d | _ d S r1   r   r=   r    r    r!   resetr  s    zMovingAverage.resetN)	r<   rU   rV   rW   __annotations__r9   r   r   r   r    r    r    r!   r   Y  s   
r   N)r(   )0rW   
__future__r   collectionsr   typingr   r   r   r   r   r	   Zpymongor
   Zpymongo.errorsr   Zpymongo.server_selectorsr   r   r   Zpymongo.topology_descriptionr   r\   rc   rj   rm   ro   r2   r#   Z_HedgerB   r"   r&   r)   r-   r.   rZ   ra   ri   rl   rn   rp   r~   r   Z_MODESrt   r   r   r    r    r    r!   <module>   sH     0(1() 	>