
    i0                        S SK Jr  S SKJr  S SKJr  S SKrS SKJr  S SK	J
r
  S SKJr  S SKJr   " S	 S
\5      rSr " S S5      rg)    )annotations)Sequence)
NamedTupleN)Producer)report_collection_diff)parse_tx_spec_config)WorkerControllerc                  *    \ rS rSr% S\S'   S\S'   Srg)NodePending   r	   nodez	list[int]pending N)__name__
__module____qualname____firstlineno____annotations____static_attributes__r       X/var/www/html/ai-image-ml/venv/lib/python3.13/site-packages/xdist/scheduler/worksteal.pyr   r      s    
r   r      c                     \ rS rSrSrSSS jjr\SS j5       r\SS j5       r\SS j5       r	\SS j5       r
SS	 jr      SS
 jr S       SS jjrSS jr      SS jrSS jrSS jrSS jrSS jrSS jrSrg) WorkStealingScheduling   a  Implement work-stealing scheduling.

Initially, tests are distributed evenly among all nodes.

When some node completes most of its assigned tests (when only one pending
test remains), an attempt is made to reassign ("steal") some tests from
other nodes to this node.

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.

:steal_requested_from_node: The node to which the current "steal" request
   was sent. ``None`` if there is no request in progress. Only one request
   can be in progress at any time, the scheduler doesn't send multiple
   simultaneous requests.
Nc                    [        [        U5      5      U l        0 U l        0 U l        / U l        S U l        Uc  [        S5      U l        OUR                  U l        Xl
        S U l        g )Nworkstealsched)lenr   numnodesnode2collectionnode2pendingr   
collectionr   logr   configsteal_requested_from_node)selfr$   r#   s      r   __init__WorkStealingScheduling.__init__C   s^    089BD?A"$,0; 01DH))DHBF&r   c                H    [        U R                  R                  5       5      $ )z%A list of all nodes in the scheduler.)listr!   keysr&   s    r   nodesWorkStealingScheduling.nodesP   s     D%%**,--r   c                F    [        U R                  5      U R                  :  $ )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,   s    r   collection_is_completed.WorkStealingScheduling.collection_is_completedU   s     4''(DMM99r   c                    U R                   (       d  gU R                  (       a  gU R                  b  gU R                  R	                  5        H  n[        U5      [        :  d  M    g   g)z9Return True if all tests have been executed by the nodes.FT)r0   r   r%   r!   valuesr   MIN_PENDINGr&   r   s     r   tests_finished%WorkStealingScheduling.tests_finished_   sV     ++<<))5((//1G7|{* 2 r   c                |    U R                   (       a  gU R                  R                  5        H  nU(       d  M    g   g)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!   r3   r5   s     r   has_pending"WorkStealingScheduling.has_pendingm   s4     <<((//1Gw 2 r   c                D    XR                   ;  d   e/ U R                   U'   g)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   s     r   add_nodeWorkStealingScheduling.add_node|   s&     ,,,,,"$$r   c                   XR                   ;   d   eU R                  (       a  U R                  (       d   eX R                  :w  a}  [        [	        U R
                  R                  5       5      5      n[        U R                  X#R                  R                  UR                  R                  5      nU R                  U5        g[        U5      U R
                  U'   g)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!   r0   r"   nextiterr    r+   r   gatewayidr#   r*   )r&   r   r"   
other_nodemsgs        r   add_node_collection*WorkStealingScheduling.add_node_collection   s     ((((('' ??"?__,!$t';';'@'@'B"CD
,OOZ1C1C1F1F %)*%5T"r   c                `    U R                   U   R                  U5        U R                  5         g)zaMark test item as completed by node.

This is called by the ``DSession.worker_testreport`` hook.
N)r!   removecheck_schedule)r&   r   
item_indexdurations       r   mark_test_complete)WorkStealingScheduling.mark_test_complete   s)     	$&&z2r   c                    U R                   c   eU R                  R                  SU R                   R                  U5      5        U R	                  5         g )Nr   )r"   r   insertindexrI   )r&   items     r   mark_test_pending(WorkStealingScheduling.mark_test_pending   sG    ***OO!!$'	
 	r   c                   XR                   L d   eSU l         [        U5      nU R                  U    Vs/ s H  oDU;  d  M
  UPM     snU R                  U'   U R                  R	                  U5        U R                  5         gs  snf )zyNode returned some test indices back in response to 'steal' command.

This is called by ``DSession.worker_unscheduled``.
N)r%   setr!   r   extendrI   )r&   r   indicesindices_setis        r   remove_pending_tests_from_node5WorkStealingScheduling.remove_pending_tests_from_node   s     55555)-&'l((.#
.!;2FA.#
$ 	G$	#
s   	BBc                :  ^ U R                   R                  5        VVs/ s H#  u  pUR                  (       a  M  [        X5      PM%     snnmSU4S jjnU" 5       nU(       d  gU R                  (       aZ  [        U5       H<  u  pQ[        U5      U-
  n[        U R                  5      U-  nU R                  X5        M>     U" 5       nU(       d  gU R                  b  g[        TS SS9nUc  Sn	OH[        S[        UR                  5      [        -
  5      n
[        [        UR                  5      S-  U
5      n	U	S:X  a  U H  nUR                  5         M     gUc   eUR                  R                  UR                  U	* S 5        UR                  U l        gs  snnf )z(Reschedule tests/perform load balancing.c                 h   > T V Vs/ s H  u  p[        U5      [        :  d  M  U PM     snn $ s  snn f N)r   r4   )r   r   nodes_ups     r   get_idle_nodes=WorkStealingScheduling.check_schedule.<locals>.get_idle_nodes   s(    .6Uh]T#g,:TDhUUUs   ..Nc                ,    [        U R                  5      $ r^   )r   r   )node_pendings    r   <lambda>7WorkStealingScheduling.check_schedule.<locals>.<lambda>   s    s<3G3G/Hr   )keydefaultr   r   returnzlist[WorkerController])r!   itemsshutting_downr   r   	enumerater   _send_testsr%   maxr4   minshutdownr   
send_steal)r&   r   r   r`   
idle_nodesrY   nodes_remainingnum_send
steal_from	num_steal	max_stealr_   s              @r   rI   %WorkStealingScheduling.check_schedule   s{    "&!2!2!8!8!:
!:%% 'K&!:
	V $%
<<$Z0"%j/A"5t||,?  0 1
 ()J ))5 HRV

 I
 As:#5#56DEIC
 2 23q8)DI> # #%%%"":#5#5yjk#BC)3&i
s
   FFc                ,   U R                   R                  U5      nU(       a.  U R                  c   eU R                  UR                  S5         nOSnU R                  R	                  U5        U R
                  UL a  SU l        U R                  5         U$ )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   rV   r%   rI   )r&   r   r   	crashitems       r   remove_node"WorkStealingScheduling.remove_node   s     ##''- ??...A7IIG$ ))T1-1D*r   c                   U R                   (       d   eU R                  b  U R                  5         gU R                  5       (       d  U R	                  S5        g[        [        U R                  R                  5       5      5      U l        [        [        U R                  5      5      U R                  SS& U R                  (       d  gU R                  5         g)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**)r0   r"   rI   !_check_nodes_have_same_collectionr#   r?   r@   r    r3   ranger   r   r,   s    r   scheduleWorkStealingScheduling.schedule  s     ++++ ??&!5577HHBC tD$8$8$?$?$ABCDOO 45Qr   c                    U R                   S U nU(       a?  U R                   S U2	 U R                  U   R                  U5        UR                  U5        g g r^   )r   r!   rV   send_runtest_some)r&   r   numtests_per_nodes       r   rm   "WorkStealingScheduling._send_tests8  sQ    ds+TcT"d#**>:"">2 r   c                   [        U R                  R                  5       5      nUS   u  p#SnUSS  H  u  pV[        X6UR                  R
                  UR                  R
                  5      nU(       d  MC  SnU R                  U5        U R                  c  Me  [        R                  " UR                  R
                  SU/ S9nU R                  R                  R                  US9  M     U$ )	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   T   NFfailed)nodeidoutcomelongreprresult)report)r*   r    rj   r   rA   rB   r#   r$   pytestCollectReporthookpytest_collectreport)	r&   node_collection_items
first_nodecolsame_collectionr   r"   rD   reps	            r   r   8WorkStealingScheduling._check_nodes_have_same_collection?  s     !%T%9%9%?%?%A B/2
 5ab 9D(!3!3!6!6C s"';;* ..#|| (!$!	C KK$$999E !:  r   )r"   r$   r#   r    r!   r   r   r%   r^   )r$   zpytest.Configr#   zProducer | Noneri   Nonerh   )ri   bool)r   r	   ri   r   )r   r	   r"   zSequence[str]ri   r   )r   r	   rJ   intrK   zfloat | Noneri   r   )rQ   strri   r   )r   r	   rW   zSequence[int]ri   r   )ri   r   )r   r	   ri   z
str | None)r   r	   r   r   ri   r   )r   r   r   r   __doc__r'   propertyr-   r0   r6   r9   r<   rE   rL   rR   rZ   rI   r|   r   rm   r   r   r   r   r   r   r      s    )VG . . : :    
%6$62?6	60 QU$25AM	  
	&69p<<3r   r   )
__future__r   collections.abcr   typingr   r   xdist.remoter   xdist.reportr   xdist.workermanager   r	   r   r4   r   r   r   r   <module>r      s<    " $   ! / 3 /*  B Br   