U
    Lk7g&                     @   s   d dl Z d dl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 d dlmZ d	ZG d
d dejZe Zdd Zdd Zdd ZG dd dZG dd dZe jdd ZG dd dZG dd dZedd Zedd ZdS )     N)contextmanager)ReadConcern)WriteConcern)_no_dereference_for_fields)_import_class)DEFAULT_CONNECTION_NAMEget_db)count_documents)	switch_dbswitch_collectionno_dereferenceno_sub_classesquery_counterset_write_concernset_read_write_concern!no_dereferencing_active_for_classc                   @   s   e Zd Zdd ZdS )MyThreadLocalsc                 C   s
   i | _ d S N)no_dereferencing_classself r   @/tmp/pip-unpacked-wheel-n1etwkgt/mongoengine/context_managers.py__init__   s    zMyThreadLocals.__init__N)__name__
__module____qualname__r   r   r   r   r   r      s   r   c                 C   s
   | t jkS r   )thread_localsr   clsr   r   r   r   "   s    r   c                 C   s$   t j| d t j|   d7  < d S )Nr      )r   r   
setdefaultr   r   r   r   $_register_no_dereferencing_for_class&   s    r"   c                 C   s0   t j|   d8  < t j|  dkr,t j|  d S )Nr    r   )r   r   popr   r   r   r   &_unregister_no_dereferencing_for_class+   s    r$   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	r
   a  switch_db alias context manager.

    Example ::

        # Register connections
        register_connection('default', 'mongoenginetest')
        register_connection('testdb-1', 'mongoenginetest2')

        class Group(Document):
            name = StringField()

        Group(name='test').save()  # Saves in the default db

        with switch_db(Group, 'testdb-1') as Group:
            Group(name='hello testdb!').save()  # Saves in testdb-1
    c                 C   s*   || _ | | _|| _|jdt| _dS )zConstruct the switch_db context manager

        :param cls: the class to change the registered db
        :param db_alias: the name of the specific database to use
        db_aliasN)r   _get_collection
collectionr%   _metagetr   ori_db_alias)r   r   r%   r   r   r   r   C   s    
zswitch_db.__init__c                 C   s   | j | jjd< d| j_| jS )z4Change the db_alias and clear the cached collection.r%   N)r%   r   r(   _collectionr   r   r   r   	__enter__N   s    zswitch_db.__enter__c                 C   s   | j | jjd< | j| j_dS )z"Reset the db_alias and collection.r%   N)r*   r   r(   r'   r+   r   tvalue	tracebackr   r   r   __exit__T   s    zswitch_db.__exit__Nr   r   r   __doc__r   r,   r1   r   r   r   r   r
   1   s   r
   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	r   aD  switch_collection alias context manager.

    Example ::

        class Group(Document):
            name = StringField()

        Group(name='test').save()  # Saves in the default db

        with switch_collection(Group, 'group1') as Group:
            Group(name='hello testdb!').save()  # Saves in group1 collection
    c                 C   s"   || _ | | _|j| _|| _dS )zConstruct the switch_collection context manager.

        :param cls: the class to change the registered db
        :param collection_name: the name of the collection to use
        N)r   r&   ori_collection_get_collection_nameori_get_collection_namecollection_name)r   r   r7   r   r   r   r   h   s    
zswitch_collection.__init__c                    s&   t  fdd}| j_d j_ jS )z@Change the _get_collection_name and clear the cached collection.c                    s    j S r   )r7   r   r   r   r   r5   v   s    z9switch_collection.__enter__.<locals>._get_collection_nameN)classmethodr   r5   r+   )r   r5   r   r   r   r,   s   s
    zswitch_collection.__enter__c                 C   s   | j | j_| j| j_dS )zReset the collection.N)r4   r   r+   r6   r5   r-   r   r   r   r1   ~   s    
zswitch_collection.__exit__Nr2   r   r   r   r   r   Z   s   r   c              	   #   sn   z^| } tdtdtd  fdd| j D }t|  t|  dV  W 5 Q R X W 5 t |  X dS )zno_dereference context manager.

    Turns off all dereferencing in Documents for the duration of the context
    manager::

        with no_dereference(Group):
            Group.objects()
    ReferenceFieldGenericReferenceFieldComplexBaseFieldc                    s$   g | ]\}}t | fr|qS r   )
isinstance).0namefieldr;   r:   r9   r   r   
<listcomp>   s    z"no_dereference.<locals>.<listcomp>N)r$   r   _fieldsitemsr"   r   )r   Zderef_fieldsr   r@   r   r      s    

r   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	r   zno_sub_classes context manager.

    Only returns instances of this class and no sub (inherited) classes::

        with no_sub_classes(Group) as Group:
            Group.objects.find()
    c                 C   s   || _ d| _dS )ztConstruct the no_sub_classes context manager.

        :param cls: the class to turn querying subclasses on
        N)r   cls_initial_subclasses)r   r   r   r   r   r      s    zno_sub_classes.__init__c                 C   s   | j j| _| j jf| j _| j S )z8Change the objects default and _auto_dereference values.)r   _subclassesrD   Z_class_namer   r   r   r   r,      s    
zno_sub_classes.__enter__c                 C   s   | j | j_dS )z/Reset the default and _auto_dereference values.N)rD   r   rE   r-   r   r   r   r1      s    zno_sub_classes.__exit__Nr2   r   r   r   r   r      s   r   c                   @   s   e Zd ZdZef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d Zdd Zdd Zdd Zdd Zdd ZdS )r   a  Query_counter context manager to get the number of queries.
    This works by updating the `profiling_level` of the database so that all queries get logged,
    resetting the db.system.profile collection at the beginning of the context and counting the new entries.

    This was designed for debugging purpose. In fact it is a global counter so queries issued by other threads/processes
    can interfere with it

    Usage:

    .. code-block:: python

        class User(Document):
            name = StringField()

        with query_counter() as q:
            user = User(name='Bob')
            assert q == 0       # no query fired yet
            user.save()
            assert q == 1       # 1 query was fired, an 'insert'
            user_bis = User.objects().first()
            assert q == 2       # a 2nd query was fired, a 'find_one'

    Be aware that:

    - Iterating over large amount of documents (>101) makes pymongo issue `getmore` queries to fetch the next batch of documents (https://www.mongodb.com/docs/manual/tutorial/iterate-a-cursor/#cursor-batches)
    - Some queries are ignored by default by the counter (killcursors, db.system.indexes)
    c                 C   s>   t |d| _d | _d| _dd| jj iddiddid| _d S )	N)aliasr   z$nez%s.system.indexesZkillcursorsz$existsF)nsopzcommand.killCursors)r   dbinitial_profiling_level_ctx_query_counterr>   _ignored_query)r   rF   r   r   r   r      s    zquery_counter.__init__c                 C   s<   | j ddi}|d | _| j jj  | j ddi d S )Nprofiler   was   )rI   commandrJ   systemrM   Zdrop)r   Zprofile_update_resr   r   r   _turn_on_profiling   s    
z query_counter._turn_on_profilingc                 C   s   | j d| ji d S )NrM   )rI   rP   rJ   r   r   r   r   _resets_profiling   s    zquery_counter._resets_profilingc                 C   s   |    | S r   )rR   r   r   r   r   r,      s    zquery_counter.__enter__c                 C   s   |    d S r   )rS   r-   r   r   r   r1      s    zquery_counter.__exit__c                 C   s   |   }||kS r   
_get_count)r   r/   counterr   r   r   __eq__   s    zquery_counter.__eq__c                 C   s   |  | S r   )rW   r   r/   r   r   r   __ne__   s    zquery_counter.__ne__c                 C   s   |   |k S r   rT   rX   r   r   r   __lt__  s    zquery_counter.__lt__c                 C   s   |   |kS r   rT   rX   r   r   r   __le__  s    zquery_counter.__le__c                 C   s   |   |kS r   rT   rX   r   r   r   __gt__  s    zquery_counter.__gt__c                 C   s   |   |kS r   rT   rX   r   r   r   __ge__
  s    zquery_counter.__ge__c                 C   s   |   S r   rT   r   r   r   r   __int__  s    zquery_counter.__int__c                 C   s   d|    S )z,repr query_counter as the number of queries.z%srT   r   r   r   r   __repr__  s    zquery_counter.__repr__c                 C   s*   t | jjj| j| j }|  jd7  _|S )zGet the number of queries by counting the current number of entries in db.system.profile
        and substracting the queries issued by this context. In fact everytime this is called, 1 query is
        issued so we need to balance that
        r    )r	   rI   rQ   rM   rL   rK   )r   countr   r   r   rU     s    zquery_counter._get_countN)r   r   r   r3   r   r   rR   rS   r,   r1   rW   rY   rZ   r[   r\   r]   r^   r_   rU   r   r   r   r   r      s   r   c                 c   s2   t | jj }|| | jtf |dV  d S )N)write_concern)dictra   documentrC   updatewith_optionsr   )r'   write_concernsZcombined_concernsr   r   r   r   #  s    
r   c                 c   sd   t | jj }|d k	r"|| t | jj }|d k	rD|| | jtf |tf |dV  d S )N)ra   read_concern)	rb   ra   rc   rC   rd   rg   re   r   r   )r'   rf   Zread_concernsZcombined_write_concernsZcombined_read_concernsr   r   r   r   *  s    

r   )
contextlib	threadingr   Zpymongo.read_concernr   Zpymongo.write_concernr   Zmongoengine.base.fieldsr   Zmongoengine.commonr   Zmongoengine.connectionr   r   Zmongoengine.pymongo_supportr	   __all__localr   r   r   r"   r$   r
   r   r   r   r   r   r   r   r   r   r   <module>   s0   )*
 b
