§
    ÕóÉjM3  ã                  óÜ   — 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	 ddl
mZ  G d„ d	¦  «        Z G d
„ d¦  «        Zej        dd„¦   «         Z G d„ d¦  «        Zd„ Z	 dd„Z	 dd„ZdS )aC  Deterministic cancellation testing for the asyncio dialects.

An asyncio task may be cancelled at any ``await``, and reproducing a
specific one of those points by racing ``task.cancel()`` against real
network traffic is unreliable.  Here the choice is made exact.
:func:`.greenlet_spawn` drives every await performed by an asyncio-driven
SQLAlchemy operation, ``connect()`` included, so instrumenting the greenlet
it switches into gives a stable enumeration of a scenario's IO points, and
cancelling at point ``N`` is reproducible run to run.

The cancellation is delivered to the task the application is running,
which is not necessarily the task performing the await; SQLAlchemy runs
some operations, such as the ``close()`` performed by an async context
manager on exit, in a task of its own.

Two things are asserted afterwards:

* Every DBAPI connection the pool creates is eventually closed through
  ``Pool._close_connection()``.  A connection that is never closed is
  leaked; for an asyncio driver the garbage collector cannot close it
  either, since closing requires the event loop.

* Once the application's task has finished and garbage has been
  collected, no pool slot is still checked out.  A slot still checked out
  at that point is lost: either nothing remains that could return it, or
  it is being returned by a task SQLAlchemy left running in the
  background, which the application has no way to wait for.  In the
  latter case, an application that disposes of its engine at that point
  has the connection returned into the disposed pool afterwards, where
  nothing closes it.

é    )ÚannotationsN)Úmocké   )Úexc)Úconcurrencyc                  ó"   — e Zd ZdZdZd„ Zd„ ZdS )Ú_InstrumentedAwaitablez6An awaitable that invokes ``hook`` when it is awaited.©Ú	awaitableÚhookc                ó"   — || _         || _        d S ©Nr
   )Úselfr   r   s      ú„/home/lighthouse/badge-speaker-benchmark/.benchmark-venv-community/lib64/python3.11/site-packages/sqlalchemy/testing/cancellation.pyÚ__init__z_InstrumentedAwaitable.__init__:   s   € Ø"ˆŒØˆŒ	ˆ	ˆ	ó    c                óh   — |                       | j        ¦  «         | j                             ¦   «         S r   )r   r   Ú	__await__©r   s    r   r   z _InstrumentedAwaitable.__await__>   s*   € Ø�	Š	�$”.Ñ!Ô!Ð!ØŒ~×'Ò'Ñ)Ô)Ð)r   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú	__slots__r   r   © r   r   r	   r	   5   s=   € € € € € Ø@Ð@à%€Iðð ð ð*ð *ð *ð *ð *r   r	   c                  óR   — e Zd ZdZd	d„Zed„ ¦   «         Zed„ ¦   «         Zd„ Zd„ Z	dS )
ÚAwaitPointsaq  Counts await points, and cancels the running task at each of the
    ones given.

    ``cancel_at`` is a tuple of await point numbers; each one is a separate
    ``task.cancel()`` call.  asyncio delivers a given cancellation exactly
    once, so a second number models a second, independent canceller
    reaching a task that is still cleaning up after the first.

    r   c                ó>   — d| _         || _        g | _        d | _        d S ©Nr   )Ú_countÚ
_cancel_atÚ_cancelled_atÚ_task)r   Ú	cancel_ats     r   r   zAwaitPoints.__init__N   s#   € ØˆŒØ#ˆŒØˆÔØˆŒ
ˆ
ˆ
r   c                ó   — | j         S )z*The number of await points counted so far.)r    r   s    r   ÚcountzAwaitPoints.countT   s   € ð Œ{Ðr   c                ó*   — t          | j        ¦  «        S )zMDescriptions of the awaitables at which a cancellation was
        delivered.)Útupler"   r   s    r   Úcancelled_atzAwaitPoints.cancelled_atZ   s   € õ
 �TÔ'Ñ(Ô(Ð(r   c                óB   — t          j        |¦  «        | _        | j        S )zZRun ``coro`` in a new task, which is the task that cancellations
        are delivered to.)ÚasyncioÚcreate_taskr#   )r   Úcoros     r   r,   zAwaitPoints.create_taska   s   € õ Ô(¨Ñ.Ô.ˆŒ
ØŒzÐr   c           	     ó4  — | xj         dz  c_         | j         | j        vrd S | j                             t	          |dt          |¦  «        ¦  «        ¦  «         | j        �| j        nt          j        ¦   «         }|€
J d¦   «         ‚| 	                    ¦   «          d S )Né   r   zQcancellation can only be delivered to an operation running within an asyncio task)
r    r!   r"   ÚappendÚgetattrÚreprr#   r+   Úcurrent_taskÚcancel)r   r   Útasks      r   Ú__call__zAwaitPoints.__call__h   sŸ   € ØˆŒ�qÑˆŒØŒ;˜dœoÐ-Ð-ØˆFàÔ×!Ò!Ý�I˜~­t°I©¬Ñ?Ô?ñ	
ô 	
ð 	
ð "œZÐ3ˆtŒzˆz½Ô9MÑ9OÔ9OˆØÐÐð%ñ  ÔÐð 	�Š‰Œˆˆˆr   N©r   )
r   r   r   r   r   Úpropertyr&   r)   r,   r6   r   r   r   r   r   C   s†   € € € € € ðð ðð ð ð ð ðð ñ „Xðð
 ð)ð )ñ „Xð)ðð ð ðð ð ð ð r   r   r   c              #  ó~  ‡‡‡‡K  — t          | ¦  «        Št          j        j        }|j        Š|j        Šˆfd„Šˆˆfd„}ˆˆfd„}t          j                             |d|¦  «        5  t          j                             |d|¦  «        5  ‰V — ddd¦  «         n# 1 swxY w Y   ddd¦  «         dS # 1 swxY w Y   dS )zlCount the await points performed within the block, cancelling the
    running task at each of ``cancel_at``.c                ó6   •— | j         r|S t          |‰¦  «        S r   )Údeadr	   )ÚgreenletÚresultÚpointss     €r   Ú
instrumentz await_points.<locals>.instrument‹   s$   ø€ ð Œ=ð 	ØˆMÝ% f¨fÑ5Ô5Ð5r   c                ó0   •—  ‰|  ‰| g|¢R i |¤Ž¦  «        S r   r   )r   ÚargÚkwr?   Úreal_switchs      €€r   Úswitchzawait_points.<locals>.switch’   s/   ø€ Øˆz˜$  ¨DÐ =°3Ð =Ð =Ð =¸"Ð =Ð =Ñ>Ô>Ð>r   c                ó*   •—  ‰|  ‰| g|¢R Ž ¦  «        S r   r   )r   rA   r?   Ú
real_throws     €€r   Úthrowzawait_points.<locals>.throw•   s'   ø€ Øˆz˜$ 
 
¨4Ð 6°#Ð 6Ð 6Ð 6Ñ7Ô7Ð7r   rD   rG   N)	r   r   Ú_concurrency_shimÚ_AsyncIoGreenletrD   rG   r   ÚpatchÚobject)r$   Úgreenlet_clsrD   rG   r?   r>   rC   rF   s       @@@@r   Úawait_pointsrM   �   sƒ  øøøøè è € õ
 ˜Ñ#Ô#€FÝÔ0ÔA€LØÔ%€KØÔ#€Jð6ð 6ð 6ð 6ð 6ð?ð ?ð ?ð ?ð ?ð ?ð8ð 8ð 8ð 8ð 8ð 8õ 	Œ
×Ò˜,¨°&Ñ9Ô9ðð åŒ
×Ò˜,¨°Ñ7Ô7ðð ð ˆˆˆð	ð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð ð ð ñ ô ð ð ð ð ð ð ð øøøð ð ð ð ð ð s6   Á'"B2Â	BÂB2ÂB	ÂB2Â!B	Â"B2Â2B6Â9B6c                  ól   — e Zd ZdZd„ Zej        d„ ¦   «         Zd„ Zddœd„Z	e
d„ ¦   «         Zd	„ Zd
„ ZdS )ÚConnectionAccountinga«  Records which DBAPI connections a pool created and which it managed
    to close, and how many of the pool's slots were lost.

    Creation is observed at ``Pool._invoke_creator`` rather than through the
    ``connect`` pool event, because the ``connect`` event is itself where
    ``dialect.initialize()`` runs; a listener added there does not fire for
    precisely the connections that a failure in that event strands.

    c                óž   — || _         |j        | _        |j        | _        d| _        t          ¦   «         | _        t          ¦   «         | _        d S r   )	Ú_poolÚ_invoke_creatorÚ_real_invoke_creatorÚ_close_connectionÚ_real_close_connectionÚ_lost_slotsÚsetÚ_createdÚ_closed)r   Úpools     r   r   zConnectionAccounting.__init__ª   sA   € ØˆŒ
Ø$(Ô$8ˆÔ!Ø&*Ô&<ˆÔ#ØˆÔÝ™œˆŒÝ‘u”uˆŒˆˆr   c              #  ó   K  — t           j                             | j        d| j        ¦  «        5  t           j                             | j        d| j        ¦  «        5  | V — ddd¦  «         n# 1 swxY w Y   ddd¦  «         dS # 1 swxY w Y   dS )z4Account for the pool's connections within the block.rR   rT   N)r   rJ   rK   rQ   rR   rT   r   s    r   ÚstartzConnectionAccounting.start²   s  è è € õ
 ŒJ×ÒØ”
Ð-¨tÔ/Cñô ð	ð 	õ ŒJ×ÒØ”
Ð/°Ô1Gñô ð		ð 	ð ˆJˆJˆJð	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	ð 	ð 	ð 	øøøð 	ð 	ð 	ð 	ð 	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	øøøð 	ð 	ð 	ð 	ð 	ð 	s5   ®,BÁA+ÁBÁ+A/	Á/BÁ2A/	Á3BÂBÂ
Bc                ód   — |                       |¦  «        }| j                             |¦  «         |S r   )rS   rX   Úadd)r   ÚrecÚdbapi_connections      r   rR   z$ConnectionAccounting._invoke_creatorÀ   s3   € Ø×4Ò4°SÑ9Ô9ÐØŒ×ÒÐ*Ñ+Ô+Ð+ØÐr   F©Ú	terminatec               óî   — 	 |                       ||¬¦  «         nA# t          j        $ r/ |r+| j        j        j        r| j                             |¦  «         ‚ w xY w| j                             |¦  «         d S )Nra   )rU   r+   ÚCancelledErrorrQ   Ú_dialectÚhas_terminaterY   r^   )r   r`   rb   s      r   rT   z&ConnectionAccounting._close_connectionÅ   s”   € ð	Ø×'Ò'Ð(8ÀIÐ'ÑNÔNÐNÐNøÝÔ%ð 	ð 	ð 	ð ð 3˜TœZÔ0Ô>ð 3Ø”× Ò Ð!1Ñ2Ô2Ð2Øð	øøøð 	Œ×ÒÐ)Ñ*Ô*Ð*Ð*Ð*s	   ‚ š>Ac                ó    — | j         | j        z
  S )z<Connections that were created but never successfully closed.)rX   rY   r   s    r   ÚleakedzConnectionAccounting.leakedÐ   s   € ð Œ}˜tœ|Ñ+Ð+r   c                óh   — t          j        ¦   «          | j                             ¦   «         | _        dS )a`  Record the pool slots still checked out.

        To be called once the application's task is done, and before the
        engine is disposed.  Every :class:`._ConnectionFairy` the task made
        is gone once garbage is collected, and :func:`._finalize_fairy` has
        returned its record; a record still checked out after that is lost.

        N)ÚgcÚcollectrQ   Ú
checkedoutrV   r   s    r   Úrecord_lost_slotsz&ConnectionAccounting.record_lost_slotsÖ   s+   € õ 	Œ
‰ŒˆØœ:×0Ò0Ñ2Ô2ˆÔÐÐr   c                óæ   — g }| j         r?|                     t          | j         ¦  «        › dt          | j        ¦  «        › d�¦  «         | j        dk    r|                     | j        › d�¦  «         |S )zpReturn a list of strings describing each leaked connection and
        lost pool slot, empty if there were none.ú of z= connection(s) were left open with nothing able to close themr   zD pool slot(s) were left checked out with nothing able to return them)rh   r0   ÚlenrX   rV   )r   Úfailuress     r   Úget_failure_messagesz)ConnectionAccounting.get_failure_messagesâ   s¢   € ð ˆØŒ;ð 	Ø�OŠOÝ�t”{Ñ#Ô#ð Að A­¨T¬]Ñ);Ô);ð Að Að Añô ð ð Ô˜aÒÐØ�OŠOØÔ#ð 3ð 3ð 3ñô ð ð ˆr   N)r   r   r   r   r   Ú
contextlibÚcontextmanagerr\   rR   rT   r8   rh   rm   rr   r   r   r   rO   rO   Ÿ   s´   € € € € € ðð ðð ð ð Ôðð ñ Ôðð ð  ð  ð
 @Eð 	+ð 	+ð 	+ð 	+ð 	+ð ð,ð ,ñ „Xð,ð

3ð 
3ð 
3ðð ð ð ð r   rO   c                óN   — t          | j        ¦  «                             ¦   «         S )z�Account for the DBAPI connections created by ``engine``'s pool.

    Returns a context manager yielding a :class:`.ConnectionAccounting`.

    )rO   rZ   r\   )Úengines    r   Úconnection_accountingrw   ô   s    € õ   ¤Ñ,Ô,×2Ò2Ñ4Ô4Ð4r   Fc              ƒ  ó>  K  — |s|r
J d¦   «         ‚ | ¦   «         }t          |¦  «        5 }|r- ||¦  «        ƒ d{V —† |r|j                             d¦  «         t          |¦  «        5 }	 |                      ||¦  «        ¦  «        ƒ d{V —† n# t
          $ r |j        s‚ Y nw xY wddd¦  «         n# 1 swxY w Y   |                     ¦   «          	 |                     ¦   «         ƒ d{V —† n# t          j
        $ r Y nw xY wt          j        ¦   «         t          j        ¦   «         hz
  }|rt          j        |d¬¦  «        ƒ d{V —† t          j        ¦   «          ddd¦  «         n# 1 swxY w Y   |d                     |                     ¦   «         ¦  «        pdfS )aç  Run ``scenario`` against a fresh engine, optionally cancelling it.

    ``engine_factory`` is a zero-argument callable returning a new
    :class:`_asyncio.AsyncEngine`; ``scenario`` is a coroutine function
    taking that engine.  If ``warm`` is True the scenario is run once
    un-cancelled first, so that the await points counted are those of
    checkout, execution and return-to-pool rather than those of
    ``connect()``.  If ``recycle`` is also True, the warmed pool is then
    invalidated, so that the checkout counted closes the old connection
    before opening its replacement.

    Returns the :class:`.AwaitPoints` for the run, and a description of
    what the cancellation cost the pool, or ``None`` if it cost nothing.

    zrecycle requires 'warm'Né   )Útimeoutz; )rw   rZ   Ú_invalidaterM   r,   ÚBaseExceptionr)   rm   Údisposer   ÚSQLAlchemyErrorr+   Ú	all_tasksr3   Úwaitrj   rk   Újoinrr   )	Úengine_factoryÚscenarior$   ÚwarmÚrecyclerv   Ú
accountingr>   Úotherss	            r   Úrun_scenariorˆ   ý   sŽ  è è € ð$ Ð9�wÐ9Ð9Ð 9Ñ9Ô9Ð9àˆ^ÑÔ€FÝ	˜vÑ	&Ô	&ð '¨*Øð 	.Ø�(˜6Ñ"Ô"Ð"Ð"Ð"Ð"Ð"Ð"Ð"Øð .Ø”×'Ò'¨Ñ-Ô-Ð-å˜)Ñ$Ô$ð 	¨ðØ×(Ò(¨¨°&Ñ)9Ô)9Ñ:Ô:Ð:Ð:Ð:Ð:Ð:Ð:Ð:Ð:øÝ ð ð ð ð Ô*ð Øðð ðøøøð	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	ð 	ð 	ð 	øøøð 	ð 	ð 	ð 	ð" 	×$Ò$Ñ&Ô&Ð&ð	Ø—.’.Ñ"Ô"Ð"Ð"Ð"Ð"Ð"Ð"Ð"Ð"øÝÔ"ð 	ð 	ð 	ð ˆDð	øøøõ Ô"Ñ$Ô$­Ô(<Ñ(>Ô(>Ð'?Ñ?ˆØð 	2Ý”,˜v¨qÐ1Ñ1Ô1Ð1Ð1Ð1Ð1Ð1Ð1Ð1Ý
Œ
‰ŒˆðO'ð 'ð 'ñ 'ô 'ð 'ð 'ð 'ð 'ð 'ð 'øøøð 'ð 'ð 'ð 'ðR �4—9’9˜Z×<Ò<Ñ>Ô>Ñ?Ô?ÐGÀ4ÐGÐGs~   ª?E)Á)B4Á+$BÂB4ÂB%Â"B4Â$B%Â%B4Â(E)Â4B8	Â8E)Â;B8	Â<E)ÃC/Ã.E)Ã/DÃ>E)Ä DÄAE)Å)E-Å0E-r/   c              ƒ  óT   ‡ ‡‡‡‡‡‡K  — g Šˆˆˆ ˆˆˆˆfd„Š ‰d¦  «        ƒ d{V —† ‰S )a  Cancel ``scenario`` at each of its await points in turn.

    With ``cancels=2``, each run that was cancelled at point ``N`` is then
    repeated with a second cancellation at each await point after ``N``,
    which is to say at each point of the cleanup the first cancellation
    set off.

    Returns a list of strings describing what went wrong, empty if every
    cancellation point was handled cleanly.  The strings are meant to be
    read in an assertion failure::

        eq_(await sweep(engine_factory, scenario), [])

    c              “  óö  •K  — t          ‰‰| ‰‰¬¦  «        ƒ d {V —†\  }}|j        }| r| d         dz   nd}t          ||dz   ¦  «        D ]®}t          ‰‰| |fz   ‰‰¬¦  «        ƒ d {V —†\  }}|�^d                     d„ | |fz   D ¦   «         ¦  «        }d                     |j        ¦  «        }‰                     d|› d|› d|› d	|› �¦  «         t          | ¦  «        dz   ‰
k     r ‰	| |fz   ¦  «        ƒ d {V —† Œ¯d S )
N)r$   r„   r…   éÿÿÿÿr/   z, c              3  ó4   K  — | ]}t          |¦  «        V — Œd S r   )Ústr)Ú.0Úis     r   ú	<genexpr>z(sweep.<locals>._sweep.<locals>.<genexpr>e  s(   è è € Ð#IÐ#I¨q¥C¨¡F¤FÐ#IÐ#IÐ#IÐ#IÐ#IÐ#Ir   zcancel at await point(s) ro   z (z): )rˆ   r&   Úranger�   r)   r0   rp   )r$   r>   Ú_Útotalr\   ÚindexÚfailureÚindexesÚ
awaitablesÚ_sweepÚcancelsr‚   r…   Úreportedrƒ   r„   s            €€€€€€€r   r˜   zsweep.<locals>._sweepR  s²  øè è € Ý&ØØØØØð
ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
‰	ˆ�ð ”ˆØ%.Ð5�	˜"” Ñ!Ð!°AˆÝ˜5 %¨!¡)Ñ,Ô,ð 	3ð 	3ˆEÝ$0ØØØ# u hÑ.ØØð%ñ %ô %ð ð ð ð ð ð ‰OˆF�Gð Ð"ØŸ)š)Ð#IÐ#I°IÀÀÑ4HÐ#IÑ#IÔ#IÑIÔI�Ø!ŸYšY vÔ':Ñ;Ô;�
Ø—’ð1°ð 1ð 1¸Uð 1ð 1Ø"ð1ð 1Ø'.ð1ð 1ñô ð õ �9‰~Œ~ Ñ! GÒ+Ð+Ø�f˜Y¨%¨Ñ1Ñ2Ô2Ð2Ð2Ð2Ð2Ð2Ð2Ð2øð!	3ð 	3r   r   Nr   )r‚   rƒ   r„   r…   r™   r˜   rš   s   `````@@r   Úsweepr›   >  sr   øøøøøøøè è € ð$ €Hð3ð 3ð 3ð 3ð 3ð 3ð 3ð 3ð 3ð 3ð 3ð8 ˆ&�‰*Œ*ÐÐÐÐÐÐÐØ€Or   r7   )r   FF)FFr/   )r   Ú
__future__r   r+   rs   rj   Úunittestr   Ú r   Úutilr   r	   r   rt   rM   rO   rw   rˆ   r›   r   r   r   ú<module>r       sr  ððð ðB #Ð "Ð "Ð "Ð "Ð "à €€€Ø Ð Ð Ð Ø 	€	€	€	Ø Ð Ð Ð Ð Ð à Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð ð*ð *ð *ð *ð *ñ *ô *ð *ð;ð ;ð ;ð ;ð ;ñ ;ô ;ð ;ð| Ôðð ð ñ Ôðð:Rð Rð Rð Rð Rñ Rô Rð Rðj5ð 5ð 5ð AFð>Hð >Hð >Hð >HðD BCð1ð 1ð 1ð 1ð 1ð 1r   