o
    %ifu3                     @  sn   d dl mZ d dlmZ d dlm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 G d	d
 d
ZdS )    )annotations)cycle)SequenceN)Producer)report_collection_diff)parse_spec_config)WorkerControllerc                   @  s   e Zd ZdZd8d9d	d
Zed:ddZed;ddZed;ddZed;ddZ	d<ddZ
d=ddZ	d>d?d"d#Zd@d&d'ZdAd*d+Zd>dBd,d-ZdCd/d0ZdDd1d2ZdEd4d5Zd;d6d7ZdS )FLoadSchedulingau  Implement load scheduling across nodes.

    This distributes the tests collected across all nodes so each test
    is run just once.  All nodes collect and submit the test suite and
    when all collections are received it is verified they are
    identical collections.  Then the collection gets divided up in
    chunks and chunks get submitted to nodes.  Whenever a node finishes
    an item, it calls ``.mark_test_complete()`` which will trigger the
    scheduler to assign more tests if the number of pending tests for
    the node falls below a low-watermark.

    When created, ``numnodes`` defines how many nodes are expected to
    submit a collection. This is used to know when all nodes have
    finished collection or how large the chunks need to be created.

    Attributes::

    :numnodes: The expected number of nodes taking part.  The actual
       number of nodes will vary during the scheduler's lifetime as
       nodes are added by the DSession as they are brought up and
       removed either because of a dead node or normal shutdown.  This
       number is primarily used to know when the initial collection is
       completed.

    :node2collection: Map of nodes and their test collection.  All
       collections should always be identical.

    :node2pending: Map of nodes and the indices of their pending
       tests.  The indices are an index into ``.pending`` (which is
       identical to their own collection stored in
       ``.node2collection``).

    :collection: The one collection once it is validated to be
       identical between all the nodes.  It is initialised to None
       until ``.schedule()`` is called.

    :pending: List of indices of globally pending tests.  These are
       tests which have not yet been allocated to a chunk for a node
       to process.

    :log: A py.log.Producer instance.

    :config: Config object, used for handling hooks.
    Nconfigpytest.ConfiglogProducer | NonereturnNonec                 C  sZ   t t|| _i | _i | _g | _d | _|d u rtd| _n|j	| _|| _
| j
d| _d S )N	loadschedmaxschedchunk)lenr   numnodesnode2collectionnode2pendingpending
collectionr   r   r   r
   	getoptionr   )selfr
   r    r   M/var/www/html/corbot_env/lib/python3.10/site-packages/xdist/scheduler/load.py__init__<   s   zLoadScheduling.__init__list[WorkerController]c                 C  s   t | j S )z%A list of all nodes in the scheduler.)listr   keysr   r   r   r   nodesI   s   zLoadScheduling.nodesboolc                 C  s   t | j| jkS )zBoolean indication initial test collection is complete.

        This is a boolean indicating all initial participating nodes
        have finished collection.  The required number of initial
        nodes is defined by ``.numnodes``.
        )r   r   r   r    r   r   r   collection_is_completedN   s   z&LoadScheduling.collection_is_completedc                 C  s:   | j sdS | jr
dS | j D ]}t|dkr dS qdS )z9Return True if all tests have been executed by the nodes.F   T)r#   r   r   valuesr   r   r   r   r   r   tests_finishedX   s   zLoadScheduling.tests_finishedc                 C  s(   | j rdS | j D ]}|r dS q
dS )zReturn True if there are pending test items.

        This indicates that collection has finished and nodes are
        still processing test items, so this can be thought of as
        "the scheduler is active".
        TF)r   r   r%   r&   r   r   r   has_pendingd   s   zLoadScheduling.has_pendingnoder   c                 C  s   || j vsJ g | j |< dS )zAdd a new node to the scheduler.

        From now on the node will be allocated chunks of tests to
        execute.

        Called by the ``DSession.worker_workerready`` hook when it
        successfully bootstraps a new node.
        N)r   )r   r)   r   r   r   add_nodes   s   	zLoadScheduling.add_noder   Sequence[str]c                 C  sr   || j v sJ | jr0| jsJ || jkr0tt| j }t| j||jj	|jj	}| 
| dS t|| j|< dS )zAdd the collected test items from a node.

        The collection is stored in the ``.node2collection`` map.
        Called by the ``DSession.worker_collectionfinish`` hook.
        N)r   r#   r   nextiterr   r   r   gatewayidr   r   )r   r)   r   
other_nodemsgr   r   r   add_node_collection   s   


z"LoadScheduling.add_node_collectionr   
item_indexintdurationfloatc                 C  s"   | j | | | j||d dS )zMark test item as completed by node.

        The duration it took to execute the item is used as a hint to
        the scheduler.

        This is called by the ``DSession.worker_testreport`` hook.
        )r5   N)r   removecheck_schedule)r   r)   r3   r5   r   r   r   mark_test_complete   s   
z!LoadScheduling.mark_test_completeitemstrc                 C  s>   | j d usJ | jd| j | | jD ]}| | qd S )Nr   )r   r   insertindexr   r8   )r   r:   r)   r   r   r   mark_test_pending   s   

z LoadScheduling.mark_test_pendingindicesSequence[int]c                 C  s   t  N)NotImplementedError)r   r)   r?   r   r   r   remove_pending_tests_from_node   s   z-LoadScheduling.remove_pending_tests_from_nodec           	      C  s   |j rdS | jrVt| j}tdt| j| d }tdt| j| d }| j| }t||k rU|dkr<t|dkr<dS |t| }tdt| | j}| |t|| n|  | 	dt| j dS )a:  Maybe schedule new items on the node.

        If there are any globally pending nodes left then this will
        check if the given node should be given any more tests.  The
        ``duration`` of the last test is optionally used as a
        heuristic to influence how many tests the node is assigned.
        Nr$      g?znum items waiting for node:)
shutting_downr   r   r   maxr   _send_testsminshutdownr   )	r   r)   r5   	num_nodesitems_per_node_minitems_per_node_maxnode_pendingnum_sendr   r   r   r   r8      s    

zLoadScheduling.check_schedule
str | Nonec                 C  sX   | j |}|s
dS | jdusJ | j|d }| j| | j D ]}| | q"|S )a  Remove a node from the scheduler.

        This should be called either when the node crashed or at
        shutdown time.  In the former case any pending items assigned
        to the node will be re-scheduled.  Called by the
        ``DSession.worker_workerfinished`` and
        ``DSession.worker_errordown`` hooks.

        Return the item which was being executing while the node
        crashed or None if the node has no more pending items.

        Nr   )r   popr   r   extendr8   )r   r)   r   	crashitemr   r   r   remove_node   s   
zLoadScheduling.remove_nodec                 C  s@  | j sJ | jdur| jD ]}| | qdS |  s"| d dS tt| j	 | _t
t| j| jdd< | js=dS | jdu rHt| j| _t| jdt| j k rlt| j}t
t| jD ]
}| t|d q`n#t| jt| j }t|d | j}t|d}| jD ]}| || q| js| jD ]}|  qdS dS )a  Initiate distribution of the test collection.

        Initiate scheduling of the items across the nodes.  If this
        gets called again later it behaves the same as calling
        ``.check_schedule()`` on all nodes so that newly added nodes
        will start to be used.

        This is called by the ``DSession.worker_collectionfinish`` hook
        if ``.collection_is_completed`` is True.
        Nz+**Different tests collected, aborting run**r$      rD   )r#   r   r!   r8   !_check_nodes_have_same_collectionr   r,   r-   r   r%   ranger   r   r   r   rG   r   rH   rF   rI   )r   r)   r!   _items_per_nodenode_chunksizer   r   r   schedule   s:   





	



zLoadScheduling.schedulenumc                 C  s@   | j d | }|r| j d |= | j| | || d S d S rA   )r   r   rQ   send_runtest_some)r   r)   r[   tests_per_noder   r   r   rG   .  s   zLoadScheduling._send_testsc           	      C  s   t | j }|d \}}d}|dd D ]0\}}t|||jj|jj}|rEd}| | | jdurEtj	|jjd|g d}| jj
j|d q|S )	zReturn True if all nodes have collected the same items.

        If collections differ, this method returns False while logging
        the collection differences and posting collection errors to
        pytest_collectreport hook.
        r   TrT   NFfailed)nodeidoutcomelongreprresult)report)r   r   itemsr   r.   r/   r   r
   pytestCollectReporthookpytest_collectreport)	r   node_collection_items
first_nodecolsame_collectionr)   r   r1   repr   r   r   rU   5  s(   

z0LoadScheduling._check_nodes_have_same_collectionrA   )r
   r   r   r   r   r   )r   r   )r   r"   )r)   r   r   r   )r)   r   r   r+   r   r   )r   )r)   r   r3   r4   r5   r6   r   r   )r:   r;   r   r   )r)   r   r?   r@   r   r   )r)   r   r5   r6   r   r   )r)   r   r   rO   )r   r   )r)   r   r[   r4   r   r   )__name__
__module____qualname____doc__r   propertyr!   r#   r'   r(   r*   r2   r9   r>   rC   r8   rS   rZ   rG   rU   r   r   r   r   r	      s,    -	



	
"

@r	   )
__future__r   	itertoolsr   typingr   re   xdist.remoter   xdist.reportr   xdist.workermanager   r   r	   r   r   r   r   <module>   s    